Files
pvemanager/WIKI.ru.md
Dilshod Rasulov abc819294c
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Failing after 1m45s
Deploy to Production / Trigger production update (push) Failing after 12s
SonarCloud / SonarCloud Analysis (push) Failing after 3m3s
fix(images): unblock guest-exec in RHEL-family cloud images
The RHEL-family qemu-guest-agent package starts the agent with an allow-list
that omits guest-exec, guest-exec-status and guest-file-* (FILTER_RPC_ARGS in
/etc/sysconfig/qemu-ga; older packages spelled the same restriction as
BLOCK_RPCS or BLACKLIST_RPC). The agent answered "The command guest-exec has
been disabled for this instance", so running scripts in a VM, assigning extra
IP addresses and growing the filesystem after a disk resize did not work on
Rocky Linux, AlmaLinux, CentOS Stream and Fedora guests.

- patch-cloud-images.sh: clear all three variants while building the mirror
  (UNBLOCK_RPC=0 opts out) and add an ONLY filter to rebuild only part of the
  catalog. Three `[ ... ] && ...` statements that aborted the script under
  set -e are now plain ifs -- the one before mirrors.json generation killed
  every run that passed BASE_URL
- builtin.py: point the five RHEL-family amd64 entries at the rebuilt mirror
  release 2026-08-22 with their new checksums. Ubuntu and Debian images are
  unaffected and stay in the previous releases, so older panel versions keep
  working
- client.py: annotate the Proxmox error with what to clear inside the guest,
  the raw message names neither the file nor the variable
- WIKI, CHANGELOG: document the restriction and the unblock command
2026-08-22 14:03:49 +05:00

2553 lines
158 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📖 PVEmanager — Документация
> Полное руководство по установке, настройке и использованию PVEmanager v1.16.0
---
## 📑 Содержание
1. [Быстрый старт](#-быстрый-старт)
2. [Установка и развёртывание](#-установка-и-развёртывание)
3. [Основные возможности](#-основные-возможности)
4. [Система уведомлений](#-система-уведомлений)
5. [Управление ВМ и контейнерами](#-управление-вм-и-контейнерами)
6. [Массовые операции](#-массовые-операции)
7. [Шаблоны ОС](#-шаблоны-ос)
8. [Images — каталог облачных образов](#-images--каталог-облачных-образов)
9. [Развёртывание LXC из CT-шаблонов](#-развёртывание-lxc-из-ct-шаблонов)
10. [App Store](#-app-store)
11. [Кластеры Proxmox](#-кластеры-proxmox)
12. [Снимки (Snapshots)](#-снимки-snapshots)
13. [Резервные копии](#-резервные-копии)
14. [IPAM](#-ipam)
15. [Удалённое выполнение команд](#-удалённое-выполнение-команд)
16. [Мониторинг](#-мониторинг)
17. [Безопасность (RBAC v2)](#-безопасность)
18. [Локализация](#-локализация)
19. [Настройки](#-настройки)
20. [Управление SSH-ключами](#-управление-ssh-ключами)
21. [Сети (SDN и интерфейсы узлов)](#сетевые-интерфейсы-узлов)
22. [Инструмент pve CLI](#-инструмент-pve-cli)
23. [Справочник API](#-справочник-api)
24. [Рабочие области (Workspaces)](#-рабочие-области-workspaces)
25. [Назначение серверов пользователю](#-назначение-серверов-пользователю)
26. [Владелец ВМ / LXC](#-владелец-вм--lxc)
27. [Квоты](#-квоты)
28. [Логи и аудит](#-логи-и-аудит)
29. [Устранение неполадок](#-устранение-неполадок)
30. [FAQ](#-faq)
---
## 🚀 Быстрый старт
### Требования
- Docker и Docker Compose
- Минимум 2 ГБ RAM
- Сервер Proxmox VE с доступом к API
### Запуск за 1 минуту
```bash
# Клонировать репозиторий
git clone https://github.com/your-repo/pvemanager.git
cd pvemanager
# Скопировать и настроить переменные окружения
cp .env.example .env
cp backend/.env.example backend/.env
# Запуск
docker compose up -d
# Открыть в браузере
open http://localhost:3001
```
**Учётные данные по умолчанию:**
- Логин: `admin`
- Пароль: `admin123`
> ⚠️ Обязательно смените пароль после первого входа!
---
## 📦 Установка и развёртывание
### Варианты развёртывания
#### 1. Standalone (разработка)
```bash
docker compose up -d
```
- Порт UI: 3001 (frontend), порт API: 8000 (backend)
- Без обратного прокси NGINX
- Подходит для локальной разработки
#### 2. С NGINX (HTTP)
```bash
./deploy.sh
# Выбрать вариант 2
```
#### 3. С NGINX + SSL (продакшн)
```bash
./deploy.sh
# Выбрать вариант 3
# Указать домен и 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`)
```bash
# База данных
POSTGRES_PASSWORD=your_secure_password
# Часовой пояс
TZ=Your/Timezone
# Отключить проверку обновлений (для приватных репо без токена)
DISABLE_UPDATE_CHECK=false
# Токен GitHub для доступа к приватному репозиторию (опционально)
GITHUB_TOKEN=
```
#### Backend (`backend/.env`)
```bash
# Секретный ключ (сгенерируйте уникальный! минимум 32 символа)
SECRET_KEY=your-very-long-secret-key-change-me
# Пароль администратора по умолчанию — ОБЯЗАТЕЛЬНО смените перед первым развёртыванием!
ADMIN_PASSWORD=admin123
# Ключ шифрования Fernet для чувствительных полей БД (пароли Proxmox/cloud-init).
# Сгенерировать: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# ВНИМАНИЕ: храните этот ключ в безопасности — его потеря означает потерю доступа к зашифрованным паролям.
# Если не задан, пароли хранятся в открытом виде (небезопасно).
FERNET_KEY=
# Время жизни JWT-токена (в минутах). По умолчанию: 480 (8 часов)
ACCESS_TOKEN_EXPIRE_MINUTES=480
# CORS-источники (через запятую). В продакшне используйте реальный домен(ы).
CORS_ORIGINS=*
```
#### Уведомления Email и Telegram
Настройки SMTP и Telegram-бота теперь управляются через веб-интерфейс:
1. Перейдите в **Настройки → Уведомления**
2. В разделе «Настройка каналов уведомлений» заполните:
- **SMTP** — параметры вашего почтового сервера
- **Telegram** — токен бота от @BotFather
3. Нажмите кнопку **Тест** для проверки настроек
---
## 📚 Справочник API
В этом разделе перечислены ключевые эндпоинты API. Все они требуют JWT-аутентификации.
### Аутентификация
- `POST /api/auth/login` — возвращает `access_token`
### Обновления системы
- `GET /settings/api/version` — текущая версия
- `GET /settings/api/updates/repository` — получить URL git-репозитория
- `PUT /settings/api/updates/repository` — задать URL git-репозитория
- `GET /settings/api/updates/check` — проверить наличие обновлений
- `POST /settings/api/updates/perform` — запустить обновление
### Серверы Proxmox
- `GET /api/servers` — список серверов
- `POST /api/servers` — добавить сервер (`auto_create_token` создаёт API-токен по логину/паролю)
- `PUT /api/servers/{id}` — обновить сервер
- `DELETE /api/servers/{id}` — удалить сервер
- `POST /api/servers/{id}/test` — проверить подключение
- `POST /api/servers/{id}/provision-token` — перевести сервер с пароля на автосозданный API-токен
### Виртуальные машины
- `GET /api/servers/{id}/resources` — список ВМ
- `POST /api/servers/{id}/vm/{vmid}/start?node={node}` — запустить ВМ
- `POST /api/servers/{id}/vm/{vmid}/stop?node={node}` — остановить ВМ
- `POST /api/servers/{id}/vm/{vmid}/restart?node={node}` — перезагрузить ВМ
- `GET /api/servers/{id}/vm/{vmid}/status?node={node}` — статус ВМ
### Контейнеры
- `GET /api/servers/{id}/resources` — список контейнеров
- `POST /api/servers/{id}/container/{vmid}/start?node={node}` — запустить контейнер
- `POST /api/servers/{id}/container/{vmid}/stop?node={node}` — остановить контейнер
- `POST /api/servers/{id}/container/{vmid}/restart?node={node}` — перезагрузить контейнер
- `GET /api/servers/{id}/container/{vmid}/status?node={node}` — статус контейнера
### Шаблоны ОС
- `GET /templates/api/groups` — список групп шаблонов
- `POST /templates/api/groups` — создать группу шаблонов
- `GET /templates/api/templates` — список шаблонов
- `POST /templates/api/templates` — создать шаблон
- `DELETE /templates/api/templates/{id}` — удалить шаблон
### IPAM
- `GET /ipam/api/networks` — список сетей
- `POST /ipam/api/networks` — создать сеть
- `DELETE /ipam/api/networks/{id}` — удалить сеть
- `GET /ipam/api/allocations` — список выделенных IP (фильтры: `network_id`, `pool_id`, `status`, `proxmox_server_id`, `proxmox_vmid`)
- `POST /ipam/api/allocations` — создать выделение IP
- `DELETE /ipam/api/allocations/{id}` — освободить IP
- `GET /ipam/api/guests/{server_id}/{vmid}/addresses` — все адреса инстанса
- `POST /ipam/api/guests/{server_id}/{vmid}/addresses` — выдать инстансу дополнительный адрес
- `POST /ipam/api/guests/{server_id}/{vmid}/addresses/{id}/apply` — повторить применение к гостю
- `POST /ipam/api/guests/{server_id}/{vmid}/addresses/{id}/primary` — сделать основным
- `DELETE /ipam/api/guests/{server_id}/{vmid}/addresses/{id}` — снять с гостя и освободить
- `POST /ipam/api/link-allocations` — просканировать гостей и зарегистрировать их адреса
### Уведомления
- `GET /api/notifications` — список уведомлений
- `PATCH /api/notifications/{id}/read` — отметить прочитанным
- `DELETE /api/notifications/{id}` — удалить уведомление
### Пользователи и роли
- `GET /api/users` — список пользователей (админ)
- `POST /api/users` — создать пользователя (админ)
- `PUT /api/users/{id}` — обновить пользователя (админ)
- `DELETE /api/users/{id}` — удалить пользователя (админ)
- `GET /api/roles` — список ролей (админ)
- `POST /api/roles` — создать роль (админ)
- `PUT /api/roles/{id}` — обновить роль (админ)
- `DELETE /api/roles/{id}` — удалить роль (админ)
### Квоты
- `GET /api/users/{id}/quota` — квота пользователя и текущее использование (требует `quota:view`)
- `PUT /api/users/{id}/quota` — задать квоту пользователя; `null` для метрики = без ограничений (требует `quota:manage`)
- `GET /api/quota` — собственная квота и использование текущего пользователя (self-service)
### Настройки
- `GET /settings/api/panel` — получить настройки панели
- `PUT /settings/api/panel` — обновить настройки панели
- `GET /settings/api/security` — получить настройки безопасности
- `PUT /settings/api/security` — обновить настройки безопасности
- `GET /settings/api/notifications` — получить настройки уведомлений
- `PUT /settings/api/notifications` — обновить настройки уведомлений
### Резервные копии и хранилища
- `GET /api/backups/storages/{server_id}` — список хранилищ
- `GET /api/backups/scan-nfs/{server_id}?server=<ip>` — сканировать NFS-сервер на доступные экспорты
- `POST /api/backups/storages/{server_id}` — добавить хранилище
- `PUT /api/backups/storages/{server_id}/{storage_id}` — обновить хранилище
- `DELETE /api/backups/storages/{server_id}/{storage_id}` — удалить хранилище
- `GET /api/backups/list/{server_id}?node=&storage=` — список файлов резервных копий
- `DELETE /api/backups/{server_id}/backup` — удалить файл резервной копии
- `POST /api/backups/create` — создать резервную копию VM/LXC (vzdump)
- `POST /api/backups/restore` — восстановить VM/LXC из резервной копии
- `GET /api/backups/task/{server_id}/{node}/{upid}` — статус/лог задачи
- `GET /api/backups/jobs` — список запланированных заданий резервного копирования
- `POST /api/backups/jobs` — создать запланированное задание
- `PUT /api/backups/jobs/{id}` — обновить задание
- `DELETE /api/backups/jobs/{id}` — удалить задание
### Высокая доступность (HA)
- `GET /api/{server_id}/ha/status` — статус HA для всех ВМ
- `GET /api/{server_id}/ha/{vm_type}/{vmid}` — статус HA для ВМ
- `POST /api/{server_id}/ha/{vm_type}/{vmid}/add` — добавить ВМ в HA
- `DELETE /api/{server_id}/ha/{vm_type}/{vmid}/remove` — убрать ВМ из HA
### SDN (Software Defined Networking)
- `GET /api/servers/{server_id}/sdn/status` — проверить доступность SDN
- `GET /api/servers/{server_id}/sdn/zones` — список зон
- `POST /api/servers/{server_id}/sdn/zones` — создать зону
- `PUT /api/servers/{server_id}/sdn/zones/{zone}` — обновить зону
- `GET /api/servers/{server_id}/sdn/vnets` — список VNet
- `POST /api/servers/{server_id}/sdn/vnets` — создать VNet
- `PUT /api/servers/{server_id}/sdn/vnets/{vnet}` — обновить VNet
- `POST /api/servers/{server_id}/sdn/vnets/{vnet}/subnets` — создать подсеть (поддерживает `create_ipam_network`)
- `DELETE /api/servers/{server_id}/sdn/vnets/{vnet}/subnets/{cidr}` — удалить подсеть
- `POST /api/servers/{server_id}/sdn/apply` — применить ожидающие изменения SDN
### Сети узлов
- `GET /api/servers/{server_id}/nodes` — список узлов сервера
### Прочее
- `POST /api/sync-vms` — принудительная немедленная синхронизация ВМ со всех серверов Proxmox
- `DELETE /proxmox/api/all-tasks/completed` — удалить все завершённые/проваленные/отменённые задачи текущего пользователя (и ProxmoxTask, и TaskQueue)
---
## 🏠 Основные возможности
### Дашборд
- **5 карточек статистики** — узлы, ВМ, контейнеры, кластеры, оповещения (в реальном времени)
- **Карточки ресурсов** — CPU, RAM, хранилище с мини-графиками (sparkline)
- **Чип статуса** в шапке — `All systems operational` / предупреждения / критические оповещения
- **Живые часы** в шапке
- Количество кластеров вычисляется автоматически из базы данных
- Счётчик оповещений из AuditLog (ошибки + критические за последние 24 ч)
- Недавние события аудита (последние 5) на дашборде
- **Селектор кластера** в боковой панели — показывает активный кластер или имя standalone
- **Бейдж версии** в шапке
### Серверы Proxmox
- Добавление нескольких серверов
- Аутентификация через API Token или пароль
- Автоматическое создание API-токена с переводом уже подключённых по паролю серверов без переподключения
- Мониторинг статуса серверов с различением недоступной ноды и отклонённых/протухших учётных данных
- Доступность storage по нодам
### Управление ВМ и LXC
- Просмотр всех виртуальных машин
- Запуск / Остановка / Перезагрузка / Выключение
- Изменение конфигурации (CPU, RAM, диск)
- VNC-консоль в браузере
- Удалённое выполнение команд
### Массовые операции
- Массовый запуск/остановка/перезагрузка/удаление ВМ и контейнеров
- Система очереди задач с отслеживанием прогресса
- Фоновая обработка
### Шаблоны ОС
- Быстрое развёртывание ВМ из шаблонов
- Кросс-узловое развёртывание шаблонов для кластеров
- Группы шаблонов и иконки
- Переустановка ВМ из шаблона
### Снимки (Snapshots)
- Создание, удаление, откат снимков VM/LXC
- Система очереди операций со снимками
- Архивирование снимков при удалении ВМ
### Резервные копии
- Ручное создание резервных копий (vzdump) для ВМ и контейнеров
- Просмотр и удаление копий по хранилищу/узлу
- Восстановление VM/LXC из копии с опциональным новым VMID
- **Запланированные задания** с cron-выражениями
- Политики хранения копий (keep_last, keep_daily, keep_weekly, keep_monthly)
- Управление пулами хранилищ (добавить, изменить, удалить)
- Отслеживание прогресса задач через UPID
### Высокая доступность (HA)
- Просмотр статуса HA для всех ВМ на сервере
- Добавление/удаление ВМ из групп Proxmox HA
- Статус HA и информация о группе для каждой ВМ
### IPAM (управление IP-адресами)
- Управление сетями и подсетями
- **Привязка к серверу и узлу** — сети привязываются к конкретному серверу и узлу Proxmox, чтобы исключить назначение IP между серверами
- **Ручной выбор сервера + сети** в модальном окне при привязке IP к VM/LXC
- Динамические селекторы сервера и узла в формах создания сети
- Автоматическое выделение IP
- Резервирование и освобождение IP
- **Несколько адресов на инстанс** — основной адрес и дополнительные, навешиваемые внутри гостя как alias
- История выделений
### Уведомления
- Оповещения о событиях ВМ в реальном времени
- Email-уведомления через SMTP
- Telegram-уведомления через Bot API
- In-App уведомления с иконкой колокольчика
### Безопасность
- RBAC v2 с атомарными правами (`resource:action`)
- Изоляция пользователей в стиле VPS (пользователи видят только свои инстансы)
- Блокировка IP (автоматическая и ручная)
- Управление сессиями
- Защита входа (блокировка, ограничение частоты)
- Журналы аудита
### Квоты
Ограничения ресурсов на пользователя, применяемые при развёртывании VM/LXC:
- **Ограничиваемые метрики** — количество инстансов, суммарные vCPU (ядра), суммарная RAM (МБ) и суммарный диск (ГБ), просуммированные по неудалённым, не-шаблонным инстансам пользователя
- **Без ограничений по умолчанию** — пользователь без строки квоты или с пустой метрикой (`NULL`) не ограничен по этой метрике; администраторы никогда не блокируются
- **Применение** — развёртывание инстанса, превышающего любой лимит, отклоняется с **HTTP 429** и двуязычным сообщением с прогнозируемым значением и лимитом
- **Управление админом** — задание лимитов на пользователя на странице **Пользователи** (диалог квот с живыми полосами использования); требует право `quota:manage` (`quota:view` для чтения)
- **Self-service** — каждый пользователь видит свои лимиты и текущее использование на странице **Настройки**, а остаток квоты показывается подсказкой в мастере создания инстанса
- Текущее использование берётся из кэша `vm_instances` (`owner_id`), где RAM хранится в МБ, а диск — в ГБ
### Мониторинг
- Метрики CPU, RAM, диска, сети в реальном времени
- Исторические графики (час, день, неделя, месяц) как для инстансов, так и для нод (вкладка «Графики» у ноды: CPU, IO delay, память, load average, сеть, swap, корневая ФС)
- Пороги оповещений по ресурсам
---
## 🔔 Система уведомлений
### Обзор
Система уведомлений обеспечивает:
- **Оповещения в реальном времени** о событиях ВМ
- **Email-уведомления** через SMTP
- **Telegram-уведомления** через Bot API
- **In-App уведомления** с иконкой колокольчика
### Типы уведомлений
| Тип | Описание | Пример |
|------|-------------|---------|
| `vm_status` | Изменения состояния ВМ | ВМ запущена/остановлена |
| `resource_alert` | Оповещения по ресурсам | CPU > 80%, RAM > 85% |
| `system` | Системные события | Ошибки подключения |
### Уровни важности
| Уровень | Цвет | Описание |
|-------|-------|-------------|
| `critical` | 🔴 Красный | Критические проблемы |
| `warning` | 🟠 Оранжевый | Предупреждения |
| `info` | 🔵 Синий | Информационные |
| `success` | 🟢 Зелёный | Успешные операции |
### Настройка Email
1. Перейдите в **Настройки** → **Уведомления**
2. В «Настройке каналов уведомлений» заполните данные SMTP:
- **SMTP-сервер** — адрес почтового сервера
- **Порт** — 465 для SSL, 587 для STARTTLS
- **Пользователь** — ваш email
- **Пароль** — пароль приложения
- **Email отправителя** — адрес отправителя
3. В «Настройках уведомлений» отметьте **Email-уведомления**
4. Нажмите **Тест** для проверки
**Поддерживаемые SMTP-серверы:**
| Сервер | Хост | Порт | TLS |
|--------|------|------|-----|
| Yandex | smtp.yandex.ru | 465 | SSL |
| Gmail | smtp.gmail.com | 587 | STARTTLS |
| Mail.ru | smtp.mail.ru | 465 | SSL |
> ⚠️ Для Yandex и Gmail используйте «пароль приложения», а не основной пароль!
### Настройка Telegram
1. Создайте бота через [@BotFather](https://t.me/BotFather)
2. В **Настройки → Уведомления** введите токен бота
3. Отправьте боту `/start`
4. Получите Chat ID через [@userinfobot](https://t.me/userinfobot)
5. В «Настройках уведомлений» включите Telegram и введите Chat ID
6. Нажмите **Проверить** для подтверждения
### Параметры уведомлений
| Параметр | Описание |
|-----------|-------------|
| Включено | Активировать канал |
| Только критические | Отправлять только критический уровень |
| Тихие часы | Период без уведомлений (напр., 23:00 – 07:00) |
### Фоновый мониторинг
Автоматически отслеживает:
- **Статус ВМ** каждые 30 секунд
- **Ресурсы** каждые 60 секунд
- **Пороги**: CPU > 80%, RAM > 85%, диск > 90%
---
## 🖥️ Управление ВМ и контейнерами
### Палитра команд (Ctrl+K)
Нажмите **Ctrl+K** (⌘K на macOS) в любом месте приложения, чтобы открыть **палитру команд** — поисковый список всех пунктов навигации. Начните печатать для фильтрации, затем <kbd>Enter</kbd> для перехода. Палитра и боковое меню используют один унифицированный реестр навигации, поэтому всегда синхронизированы.
### Действия с ВМ
Меню строки инстанса (⋯) содержит **Опции**, **Миграцию**, **Изменение ресурсов**, **Создание резервной копии** и **Удаление** наряду с действиями питания ниже. Меню остаётся открытым при живых обновлениях списка.
| Действие | Описание | Хоткей |
|--------|-------------|--------|
| Start | Запустить ВМ | - |
| Stop | Остановить (ACPI-выключение) | - |
| Restart | Перезагрузить | - |
| Force Stop | Принудительная остановка | - |
| Delete | Удалить ВМ | - |
> **Вкладки инстанса:** для **ВМ** карточки *Compute resources*, *CPU options*, *Disk management* и *Disk resize* находятся на вкладке **Hardware**; **LXC**-контейнеры (у которых нет вкладки Hardware) держат эти карточки на вкладке **Settings**.
### Массовые операции
Выберите несколько ВМ/контейнеров и выполните массовые действия:
1. Отметьте чекбокс рядом с каждой нужной ВМ
2. Используйте «Выбрать все» для выбора всех видимых ВМ
3. Нажмите кнопку действия в панели массовых операций:
- **Start All** — запустить все выбранные
- **Stop All** — остановить все выбранные
- **Restart All** — перезагрузить все выбранные
- **Delete All** — удалить все выбранные (с подтверждением)
Подробнее см. [Массовые операции](#-массовые-операции).
### Консоль (VNC / терминал)
- **QEMU-ВМ** — VNC-консоль через noVNC (графический вывод в браузере).
1. Откройте детали ВМ → нажмите **Console**, либо ПКМ по ВМ в списке → **VNC Console**
2. Полный проброс клавиатуры и мыши; поддержка полноэкранного режима
- **LXC-контейнеры** — интерактивный shell через терминал xterm.js.
1. Откройте детали контейнера → нажмите **Terminal**, либо ПКМ по контейнеру в списке → **Terminal**
2. Полный PTY: автодополнение, цвета, Unicode, изменение размера терминала при ресайзе окна
3. Использует WebSocket-протокол Proxmox `termproxy` (рукопожатие аутентификации + keepalive-пинги каждые 2 мин)
### Изменение конфигурации
```
CPU: 1–32 ядра
RAM: 512 МБ – 128 ГБ
Диск: увеличение размера (уменьшение невозможно)
```
> **Примечание (v1.2.0+):** изменение размера диска полностью асинхронно. Панель ждёт завершения задачи ресайза в Proxmox перед запуском ВМ, что предотвращает ошибки `lock file timeout`, возникавшие при немедленном запуске ВМ после запроса ресайза.
### Высокая доступность (HA)
Требуется кластер Proxmox с настроенным менеджером HA.
1. Откройте детали ВМ или контейнера
2. Перейдите на вкладку **HA**
3. Нажмите **Add to HA** и выберите HA-группу (опционально)
4. Для удаления — нажмите **Remove from HA**
> Операции панели сейчас отражают статус HA, но не заменяют конфигурацию менеджера Proxmox HA.
---
## ⚡ Массовые операции
### Обзор
Массовые операции позволяют выполнить одно и то же действие над несколькими ВМ или контейнерами одновременно. Это удобно, когда нужно запустить, остановить или перезагрузить много виртуальных машин сразу.
### Поддерживаемые действия
| Действие | Описание | Требуется подтверждение |
|--------|-------------|----------------------|
| Bulk Start | Запустить все выбранные VM/контейнеры | Нет |
| Bulk Stop | Остановить все выбранные (ACPI-выключение) | Нет |
| Bulk Restart | Перезагрузить все выбранные | Нет |
| Bulk Shutdown | Корректное выключение | Нет |
| Bulk Delete | Удалить все выбранные | Да (двойное подтверждение) |
### Как использовать
1. **Перейдите на страницу «Виртуальные машины»**
2. **Выберите ВМ** чекбоксами:
- Кликайте отдельные чекбоксы
- Или используйте «Выбрать все» для всех видимых ВМ
3. **Панель массовых действий** появляется внизу при выборе ВМ
4. **Нажмите нужное действие**
5. **Подтвердите** при запросе (для операций удаления)
### Панель задач (Task Drawer)
При запуске массовой операции **Task Drawer** автоматически открывается в боковой панели. Он показывает прогресс каждой задачи в реальном времени:
- **Выполняющиеся** задачи показывают спиннер и текущий прогресс (`completed / total`)
- **Завершённые** задачи зелёные; **проваленные** — красные
- Задачи старше **24 часов** автоматически скрываются из панели (остаются доступны на странице `/tasks`)
- Кнопка **корзины** (🗑) в шапке панели удаляет все завершённые/проваленные/отменённые задачи одним кликом
### Отслеживание UPID Proxmox
Начиная с v1.1.5, отдельные действия управления VM/LXC (start / stop / restart) связаны с реальным UPID задачи Proxmox. UPID регистрируется как запись `ProxmoxTask` и опрашивается в реальном времени, поэтому Task Drawer отражает фактическое состояние задачи на стороне Proxmox.
### Ограничения
- Одновременно выполняется только одна массовая задача
- Задачи обрабатываются последовательно
- Максимум элементов на задачу: не ограничено (но учитывайте нагрузку на сервер)
- Задачи нельзя приостановить (только отменить, если ожидают)
---
## 📋 Шаблоны ОС
### Концепция
Шаблоны ОС позволяют быстро развёртывать ВМ из предварительно настроенных шаблонов.
### Создание группы шаблонов
1. **Шаблоны ОС** → **Groups** → **Add Group**
2. Введите имя (напр., «Linux Servers»)
3. Добавьте описание
### Добавление шаблона
1. **Шаблоны ОС** → **Templates** → **Add Template**
2. Или: **Scan** → выбрать из Proxmox
### Развёртывание ВМ
1. Выберите шаблон
2. Укажите:
- Имя ВМ
- **Целевой узел** (для кластеров — выбрать, куда развернуть)
- CPU/RAM/диск
- IP-адрес (или авто из IPAM)
- SSH-ключ (опционально)
3. Нажмите **Deploy**
### Кросс-узловое развёртывание шаблонов (кластеры)
При использовании кластеров Proxmox шаблоны можно развернуть на любой узел кластера:
#### Как это работает
1. **Создание шаблона на любом узле** — шаблон изначально создаётся на одном узле кластера
2. **Выбор целевого узла** — при развёртывании ВМ выберите любой онлайн-узел кластера
3. **Автоматическая репликация** — если шаблона нет на целевом узле:
- Система клонирует шаблон на целевой узел
- Конвертирует клон в шаблон
- Отслеживает реплицированные шаблоны в базе данных
4. **Последующие развёртывания** — используют уже реплицированный шаблон (быстро)
#### Требования
- Кластер Proxmox (узлы должны быть в одном кластере)
- Рекомендуется общее хранилище (но не обязательно)
- Шаблон должен быть доступен с исходного узла
#### Пример
```
Кластер: pve1, pve2, pve3
Шаблон: Ubuntu-22.04 на pve1
Развёртывание на pve1: используется оригинальный шаблон (быстро)
Развёртывание на pve2: репликация шаблона → развёртывание (первый раз медленнее)
Развёртывание на pve3: репликация шаблона → развёртывание (первый раз медленнее)
Следующее развёртывание на pve2: используется реплицированный шаблон (быстро)
```
### Переустановка ВМ
1. Откройте детали ВМ
2. Нажмите **Reinstall**
3. Выберите шаблон
4. VMID сохраняется
---
## 📥 Images — каталог облачных образов
### Обзор
Модуль **Images** позволяет просматривать, скачивать и управлять образами ОС из облачных репозиториев. Образы можно скачивать напрямую в хранилище Proxmox и опционально автоматически конвертировать в шаблоны ВМ.
### Возможности
- **Просмотр облачных образов** — обнаружение образов ОС из стандартного репозитория Proxmox и кастомных зеркал
- **Скачивание образов** — загрузка образов `.qcow2` в любое хранилище Proxmox с отслеживанием прогресса
- **Автоконвертация в шаблон** — опциональная автоматическая конвертация скачанных образов в шаблоны ВМ Proxmox
- **Фильтрация по архитектуре** — образы фильтруются по платформе узла (x86-64, aarch64); архитектура по умолчанию соответствует выбранному узлу
- **Кастомные зеркала** — администраторы могут добавлять, редактировать и управлять кастомными источниками зеркал
- **Загрузка ISO** — установочный ISO можно скачать по URL, из сохранённого зеркала (`kind=iso`) или прямо на шаге ISO в мастере создания инстанса. Встроенный каталог намеренно ограничен `.qcow2` — у установочных ISO нет стабильной ссылки на «последнюю версию», поэтому жёстко прописанные ссылки протухали бы при каждом релизе
- **Загрузка локального файла** — ISO или шаблон LXC (`vztmpl`) можно загрузить со своего компьютера прямо в storage ноды — для образов, которые нигде публично не доступны панели
### Доступ к Images
1. Перейдите в **Infrastructure** → **Images** (боковое меню)
2. Выберите сервер и узел Proxmox
3. Просматривайте доступные образы, отфильтрованные по архитектуре
### Скачивание образа
1. Нажмите **Download** рядом с нужным образом
2. Выберите целевое хранилище
3. Опционально включите **Convert to Template** (создаёт шаблон ВМ по завершении)
4. Нажмите **Download** — прогресс отслеживается в реальном времени
5. По завершении образ доступен в хранилище для развёртывания ВМ
### Загрузка локального файла
1. Перейдите в **Infrastructure** → **Images** и выберите **Upload**
2. Укажите файл ISO или `vztmpl` и целевое хранилище
3. Панель передаёт файл на ноду в фоне; прогресс отслеживается так же, как при загрузке по URL
4. По завершении файл доступен в хранилище для развёртывания ВМ/CT
### Установка ОС с ISO
Мастер создания инстанса предлагает тип **«пустая ВМ»** для установки с ISO: создаётся ВМ с пустым диском и смонтированным на `ide2` образом, который ставится первым в порядке загрузки, — гость сразу попадает в установщик через встроенную консоль noVNC. Выбор гостя Windows автоматически задаёт прошивку OVMF, SATA-диск и сетевую карту e1000, так как у установщика ещё нет драйверов virtio.
### Извлечение и монтирование ISO
На странице ВМ диалог ISO показывает все образы, уже лежащие в хранилище ноды — включая те, что появились там до подключения ноды к панели, — и текущий смонтированный образ, если он есть:
- **Монтировать** — выберите ISO из хранилища; опционально отметьте **«загрузка с этого ISO»**, чтобы поставить CD-ROM первым в порядке загрузки.
- **Извлечь** (переименовано из «Отсоединить») — снимает смонтированный ISO; опционально отметьте **«загрузка с диска»**, чтобы поставить основной диск первым в порядке загрузки.
Любое действие, меняющее порядок загрузки, применяется через **гибридную перезагрузку**: штатное ACPI-выключение с откатом на принудительную остановку, затем запуск. Это убирает таймаут ping гостя, с которым сталкивается обычный рестарт на свежеустановленной ОС, где ещё не запущен QEMU Guest Agent.
### GitHub-зеркало с QEMU Guest Agent
Встроенный каталог образов amd64 ссылается на **GitHub-зеркало**, содержащее облачные образы с предустановленным `qemu-guest-agent`. Это даёт:
- Живые метрики на ВМ (CPU, память, заполнение диска, скорости I/O) сразу после деплоя — дополнительная установка агента не нужна
- В образах RHEL-семейства (Rocky Linux, AlmaLinux, CentOS Stream, Fedora) снято штатное ограничение RPC: пакет `qemu-guest-agent` там оставляет агенту allow-list без `guest-exec`, `guest-exec-status` и `guest-file-*` (`FILTER_RPC_ARGS` в `/etc/sysconfig/qemu-ga`), из-за чего в таких ВМ не работали выполнение скриптов, дополнительные IP-адреса и расширение ФС после ресайза диска
- Образы включают контрольную сумму **sha256**; если закешированный файл не совпадает с опубликованной суммой, панель скачает его повторно автоматически
Чтобы собрать или обновить зеркало самостоятельно, запустите вспомогательный скрипт на Linux-хосте с установленным QEMU:
```bash
bash patch-cloud-images.sh
```
Скрипт скачивает исходный `.qcow2`, устанавливает `qemu-guest-agent` через `virt-customize`, снимает блокировку RPC (отключается через `UNBLOCK_RPC=0`) и публикует патченный образ как GitHub Release.
### Кастомные зеркала (только админ)
Администраторы могут настроить кастомные репозитории образов:
1. Перейдите в **Настройки** → **Images** → **Custom Mirrors**
2. Нажмите **Add Mirror**
3. Введите URL зеркала (должен быть валидным репозиторием Proxmox)
4. Сохраните — зеркало сразу появится в браузере образов
**Формат URL зеркала:**
Валидное зеркало должно предоставлять стандартную структуру репозитория Proxmox с файлом `index.json`, содержащим метаданные образов.
### Эндпоинты API
| Метод | Эндпоинт | Описание |
|--------|----------|-------------|
| `GET` | `/api/images` | Список доступных образов |
| `GET` | `/api/images/node-arch/{server_id}/{node}` | Архитектура узла |
| `POST` | `/api/images/download` | Запустить скачивание (возвращает UPID задачи) |
| `GET` | `/api/images/mirrors` | Список кастомных зеркал (админ) |
| `POST` | `/api/images/mirrors` | Добавить кастомное зеркало (админ) |
| `PUT` | `/api/images/mirrors/{id}` | Обновить зеркало (админ) |
| `DELETE` | `/api/images/mirrors/{id}` | Удалить зеркало (админ) |
### Отслеживание задачи скачивания
Скачивание образов отслеживается как задачи Proxmox (UPID). Используйте стандартный эндпоинт задач для мониторинга прогресса:
```bash
GET /api/backups/task/{server_id}/{node}/{upid}
```
---
## 📦 Развёртывание LXC из CT-шаблонов
### Обзор
Начиная с v1.5.0, LXC-контейнеры можно создавать напрямую из файлов CT-шаблонов Proxmox (`.tar.zst`, `.tar.gz`) без необходимости заранее настраивать запись «Шаблон ОС». Это стандартный способ развернуть Debian, Ubuntu, Alpine и другие дистрибутивы из списка загрузок Proxmox.
### Просмотр доступных CT-шаблонов
CT-шаблоны берутся напрямую из хранилища сервера Proxmox:
1. Перейдите в **Instances** → **Create Instance**
2. Выберите целевой сервер Proxmox
3. Выберите тип **LXC Container**
4. Мастер получает доступные файлы `.tar.zst` / `.tar.gz` со всех хранилищ выбранного сервера
### Развёртывание LXC-контейнера
1. Шаг **Server** — выберите сервер Proxmox
2. Шаг **Type** — выберите **LXC Container**
3. Шаг **Template** — выберите CT-шаблон из списка
4. Шаг **Config** — заполните:
- Имя контейнера (hostname)
- Ядра CPU, RAM (МБ), Swap (МБ), диск (ГБ)
- Хранилище, сетевой мост
- IP-адрес / шлюз (вручную или из IPAM)
- Root-пароль и/или SSH-ключи
- Владелец (только админ)
- Опции Start after create / On-boot
5. Шаг **Confirm** — проверьте и нажмите **Deploy**
Развёртывание выполняется асинхронно; прогресс отслеживается в Task Drawer.
### SSH-ключи при развёртывании LXC
- Выберите один или несколько ключей из личной библиотеки SSH-ключей (см. [Управление SSH-ключами](#-управление-ssh-ключами))
- Админы, развёртывающие от имени другого пользователя, могут выбрать ключи из библиотеки этого пользователя
- Ключи внедряются в `authorized_keys` контейнера во время провижининга
### Переустановка LXC-контейнера (CT-шаблон)
Контейнеры, созданные из CT-шаблонов, поддерживают переустановку:
1. Откройте меню действий контейнера → **Reinstall**
2. Опционально выберите другой CT-шаблон
3. Автоматически сохраняются:
- Root-пароль
- SSH-ключи
- IP-адрес и шлюз
4. Старый контейнер уничтожается, создаётся новый с тем же VMID
> **Примечание:** `nesting=1` включается по умолчанию для всех новых LXC-контейнеров для совместимости с systemd 255+ (cgroup v2).
---
## 🛒 App Store
Каталог self-hosted приложений с установкой в один клик. Каждое приложение работает в собственном **unprivileged LXC**-контейнере с **Docker Compose** (1 приложение = 1 LXC). Каталог **мультиисточниковый**: [`runtipi/runtipi-appstore`](https://github.com/runtipi/runtipi-appstore) (~265 приложений) и опционально [`getumbrel/umbrel-apps`](https://github.com/getumbrel/umbrel-apps) (~380 приложений). Источники выбираются через `APPSTORE_SOURCES`; id приложений Umbrel имеют префикс (`umbrel-<id>`) во избежание коллизий.
### Предварительные требования
- **Золотой шаблон LXC** (Debian 12 + Docker + Compose) на каждый сервер Proxmox. Соберите его из **App Store → золотой шаблон** (`POST /api/appstore/golden-template`, выбирает сервер/узел/хранилище) — это автоматизирует ручную процедуру из [docs/golden-template.md](docs/golden-template.md). Хранилище шаблона и rootfs выбираются из выпадающих списков, заполненных реальными хранилищами выбранного сервера/ноды (по content `vztmpl`/`rootdir`), с автоподстановкой `local`/`local-lvm`, если они доступны. Полученный volid vztmpl хранится **для каждого `server_id`** (шаблон, собранный на сервере A, лежит только в хранилище A и никогда не используется для установки на сервере B); `APPSTORE_GOLDEN_TEMPLATE` — лишь последний запасной вариант для серверов без собственной сборки.
- **SSH-доступ** от панели к узлу Proxmox (используется для `pct exec` / `pct push`).
- Установлен `FERNET_KEY` (шифрует секреты приложений на диске).
### Настройки окружения
| Переменная | По умолчанию | Назначение |
|---|---|---|
| `APPSTORE_GOLDEN_TEMPLATE` | — | запасной volid золотого vztmpl, используется только если у сервера нет собственного сохранённого шаблона |
| `APPSTORE_NAMESERVER` | `1.1.1.1 8.8.8.8` | DNS, явно задаваемый на LXC App Store (и на CT сборки золотого шаблона) — без него контейнер наследует резолвер узла, который может быть недоступен из сети LXC (напр., DNS только для Tailscale на хосте) и ломает pull образов |
| `APPSTORE_SOURCES` | `runtipi` | активные источники каталога через запятую (`runtipi`, `umbrel`) |
| `RUNTIPI_APPSTORE_REPO` | `runtipi/runtipi-appstore` | источник каталога Runtipi |
| `RUNTIPI_APPSTORE_REF` | `master` | ветка/тег/коммит (рекомендуется зафиксировать) |
| `UMBREL_APPSTORE_REPO` | `getumbrel/umbrel-apps` | источник каталога Umbrel (когда включён `umbrel`) |
| `UMBREL_APPSTORE_REF` | `master` | ветка/тег/коммит Umbrel |
| `UMBREL_APPSTORE_GALLERY_CDN` | `https://getumbrel.github.io/umbrel-apps-gallery` | CDN иконок Umbrel (`<app-id>/icon.svg`, скачиваются пофайлово параллельно) |
| `APPSTORE_DATA_DIR` | `/app/data/appstore` | кэш логотипов (смонтированный volume) |
| `CATALOG_SYNC_INTERVAL_HOURS` | `24` | интервал авто-синхронизации каталога |
| `APPSTORE_HOST_ARCH` | `amd64` | целевая архитектура узла (помечает неподдерживаемые приложения) |
### Каталог
- Экран **App Store**: сетка карточек с поиском и фильтрами по категориям; кнопка **Refresh catalog** + метка времени последней синхронизации. Когда включено более одного источника, появляются **фильтр источника** и **бейдж источника** на карточке (Runtipi / Umbrel).
- Автоматическая синхронизация каждые 24 ч; повреждённая запись приложения пропускается без провала всей синхронизации. Каждый источник синхронизируется независимо, а проход *исчез → недоступно* ограничен рамками источника.
- Приложения Umbrel: метаданные из `umbrel-app.yml`, иконки из репозитория gallery; при установке runtime-сервисы Umbrel `app_proxy`/`tor` вырезаются, а порт контейнера (`app_proxy → APP_PORT`) публикуется на хосте LXC.
- Страница приложения: описание, версия, ссылка на источник, дисклеймер установки и предпросмотр compose.
### Установка
Конвейер (каждый шаг журналируется и стримится по WebSocket):
1. Клонирование золотого шаблона LXC (по умолчанию 2 vCPU / 2048 МБ / 8 ГБ, DHCP или расширенные переопределения).
2. Запуск и ожидание IP.
3. `pct push` `docker-compose.yml` + `.env` (ответы формы + сгенерированные `random`-секреты) + любые вложенные `data_files`.
4. `docker compose up -d`.
5. HTTP health-check.
6. Показ URL приложения и одноразовых учётных данных.
Проваленный шаг ставит статус `error` с логом шага; **Retry** не пересоздаёт LXC (идемпотентно по VMID) и повторно использует исходные параметры установки (сохранены в `installed_apps.install_params`), **Delete** удаляет контейнер и запись.
**Заметки о надёжности:**
- Файлы, поставляемые с приложением каталога (`data_files`), доставляются в контейнер до `docker compose up`; учитываются Umbrel `APP_PASSWORD` / `default_credentials`.
- Каталоги bind-mount хоста (`volumes:` в compose приложения, и `${APP_DATA_DIR}/...`, и относительные пути) предварительно создаются с разрешительными правами до `up` — иначе Docker создаёт их как root, и non-root пользователи контейнера (напр., `mysql` у mariadb) получают `Permission denied`.
- Поиск VMID пропускает уже занятые записи — `nextid` не резервирует номер, поэтому повтор мог бы иначе занять другой контейнер.
- `pct create` повторяет несколько раз при временной ошибке config-lock (частой сразу после удаления того же VMID, пока узел ещё стирает старый диск).
- **Delete** теперь ждёт, пока Proxmox фактически подтвердит завершение задачи destroy (до 30 мин, покрывает медленное стирание диска), прежде чем пометить приложение удалённым — иначе VMID мог бы быть переиспользован, пока узел ещё занят.
- Значения `.env`, содержащие `#`, берутся в кавычки (парсер dotenv иначе обрезал бы их как inline-комментарий); порт, явно выбранный в мастере (включая `80`), публикуется как есть.
### Трей свёрнутых операций
Долгие операции App Store — **установка** приложения и **сборка золотого шаблона** — можно свернуть вместо того, чтобы держать диалог открытым:
- Свёрнутые операции живут в **глобальном хранилище** (сохраняется в `localStorage`) и рендерятся как **трей** в макете приложения, поэтому плашка **переживает навигацию по страницам и полную перезагрузку вкладки**. Трей сам опрашивает прогресс каждой операции (REST, каждые 3 с), независимо от диалога.
- **Клик по плашке** возвращает на страницу операции и переоткрывает диалог — установка возобновляется в мастере, сборка шаблона переоткрывает диалог золотого шаблона с предвыбранным сервером.
- **Свернуть** добавляет операцию в трей и закрывает диалог; ✕ на плашке лишь удаляет плашку (операция продолжает работать на сервере). Плашки операций, которых больше нет (`404` после перезагрузки), очищаются автоматически.
### Мои приложения
Управление установленными приложениями: **Start / Stop / Restart / Logs / Delete**, живой статус и кликабельный `IP:port`.
- **Реконсиляция** запускается каждые 60 с и при открытии экрана: LXC, удалённый вне PVEmanager, помечается `orphaned`.
- **Обновление**: делает pre-update снапшот Proxmox → `docker compose pull && up -d` → health-check; хранит только последний pre-update снапшот.
- **Откат**: восстанавливает снапшот (⚠️ также откатывает **данные** приложения к моменту снапшота) и возвращает версию в базе данных.
### Права
`app:view` (каталог + Мои приложения), `app:install` (установка), `app:manage` (синхронизация, жизненный цикл, обновление, откат, удаление). Админы обходят все проверки.
### REST API
```
GET /api/appstore/catalog # список (q, category, source)
GET /api/appstore/catalog/{app_id} # детали
GET /api/appstore/catalog/{app_id}/logo # логотип (публично)
POST /api/appstore/catalog/sync # ручная синхронизация (админ)
POST /api/appstore/apps/install # установка
GET /api/appstore/apps # список установленных
POST /api/appstore/apps/{id}/action/{start|stop|restart}
GET /api/appstore/apps/{id}/logs
POST /api/appstore/apps/{id}/update
POST /api/appstore/apps/{id}/rollback
POST /api/appstore/apps/reconcile
DELETE /api/appstore/apps/{id}
POST /api/appstore/golden-template # собрать золотой vztmpl для сервера
GET /api/appstore/golden-template/status?server_id=… # шаблон сервера + последняя операция сборки
```
---
## 🔗 Кластеры Proxmox
### Обзор
PVEmanager полностью поддерживает кластеры Proxmox, позволяя управлять несколькими узлами как единой сущностью.
### Добавление кластера
1. Перейдите в **Proxmox VE** → **Add Server**
2. Введите IP-адрес **любого узла** (напр., pve1)
3. Панель автоматически обнаружит все узлы кластера
4. Все узлы появятся в селекторах узлов
### Преимущества кластера
| Возможность | Standalone | Кластер |
|---------|------------|---------|
| Кол-во узлов | 1 | Несколько |
| Кросс-узловые шаблоны | ❌ | ✅ |
| Выбор целевого узла | ❌ | ✅ |
| Автоматический failover | ❌ | ✅ (Proxmox HA) |
### Создание кластера Proxmox
Если ваши узлы ещё не в кластере:
```bash
# На первом узле (pve1)
pvecm create my-cluster
# На других узлах (pve2, pve3 и т.д.)
pvecm add 10.10.10.11 # IP pve1
```
### Кросс-узловое развёртывание шаблонов
См. [Шаблоны ОС — Кросс-узловое развёртывание](#кросс-узловое-развёртывание-шаблонов-кластеры)
### Импорт уже существующего кластера
Если узлы уже объединены в кластер в обход панели (через `pvecm` или веб-интерфейс Proxmox), сначала добавьте каждый узел в панель как обычный standalone-сервер, затем воспользуйтесь **Кластер** → **Импорт**:
1. Выберите любой из уже добавленных узлов — панель прочитает с него `cluster/config/join` и определит реальное имя кластера и список узлов
2. Совпавшие по IP или hostname серверы панели будут отмечены автоматически; узлы, ещё не добавленные в панель, будут показаны в списке, но привязать их нельзя, пока они не добавлены как серверы
3. Подтвердите — панель только привяжет совпавшие записи серверов к группе кластера в своей БД; никакая команда `pvecm` не выполняется, реальный кластер не затрагивается
Это чисто БД-аналог **«Создать кластер»**/**«Присоединить ноду»**, которые выполняют настоящие операции `pvecm create`/`pvecm add` — используйте Импорт только когда узлы уже реально объединены в кластер.
---
## 📸 Снимки (Snapshots)
### Обзор
Снимки позволяют сохранять и восстанавливать состояние ВМ и контейнеров в любой момент времени.
### Создание снимка
1. Откройте детали ВМ или контейнера
2. Перейдите на вкладку **Snapshots**
3. Нажмите **Create Snapshot**
4. Введите имя и описание
5. Опционально включите состояние RAM (только QEMU-ВМ)
### Управление снимками
| Действие | Описание |
|--------|-------------|
| Create | Сохранить текущее состояние ВМ |
| Rollback | Восстановить к предыдущему состоянию снимка |
| Delete | Удалить существующий снимок |
> ⚠️ Откат отменит все изменения, сделанные после создания снимка!
### Очередь снимков
Операции со снимками обрабатываются через систему очереди задач. Прогресс отслеживается, результаты сохраняются для каждого элемента.
### Эндпоинты API
```bash
# Список снимков
GET /api/{server_id}/vm/{vmid}/snapshots?node={node}
# Создать снимок
POST /api/{server_id}/vm/{vmid}/snapshots?node={node}
# Удалить снимок
DELETE /api/{server_id}/vm/{vmid}/snapshots/{snapname}?node={node}
# Откатить к снимку
POST /api/{server_id}/vm/{vmid}/snapshots/{snapname}/rollback?node={node}
```
> Для LXC-контейнеров используйте `/container/` вместо `/vm/` в эндпоинтах выше.
---
## 💾 Резервные копии
### Обзор
PVEmanager предоставляет полный интерфейс управления резервными копиями на базе Proxmox `vzdump`. Можно создавать, просматривать, восстанавливать и удалять копии, а также настраивать автоматические запланированные задания.
### Управление хранилищами
Перед созданием копий убедитесь, что целевое хранилище настроено в Proxmox.
| Действие | Описание |
|--------|-------------|
| List storages | Просмотр всех хранилищ сервера |
| Add storage | Добавить пул хранилища (directory, NFS, CIFS и т.д.) |
| Edit storage | Изменить конфигурацию хранилища |
| Delete storage | Удалить хранилище из Proxmox |
### Создание резервной копии
1. Перейдите на страницу **Backups**
2. Выберите **сервер** и **узел**
3. Выберите **хранилище**, куда сохранить копию
4. Выберите **VM/контейнер** (по VMID)
5. Настройте опции:
- **Режим**: `snapshot` (по умолчанию), `suspend` или `stop`
- **Сжатие**: `zstd` (по умолчанию), `lzo`, `gzip`, `none`
6. Нажмите **Create Backup**
7. Возвращается UPID — используйте его для отслеживания прогресса
### Режимы резервного копирования
| Режим | Описание | Простой |
|------|-------------|----------|
| `snapshot` | Живой снимок (требует QEMU agent или LVM) | Нет |
| `suspend` | Приостановка ВМ на время копирования | Кратковременный |
| `stop` | Остановка ВМ, копирование, запуск | Полная остановка |
### Просмотр и удаление копий
- Просматривайте существующие копии по серверу, узлу и хранилищу
- Фильтруйте по VMID
- Удаляйте отдельные файлы копий
### Восстановление
1. Выберите файл копии из списка
2. Нажмите **Restore**
3. Укажите:
- **Целевое хранилище** для восстановления
- **Новый VMID** (опционально, оставить оригинал или назначить новый)
- **Start after restore** (опционально)
4. Восстановление выполняется как задача Proxmox (возвращается UPID для отслеживания)
> ⚠️ Восстановление в существующий VMID перезапишет его!
### Запланированные задания резервного копирования
Автоматизируйте регулярные копии с помощью cron-планирования.
#### Создание задания
```
Server: выбрать сервер Proxmox
Node: целевой узел
VMIDs: список через запятую (напр., 100,101,102)
Storage: назначение копии
Mode: snapshot / suspend / stop
Compress: zstd / lzo / gzip / none
Cron: стандартное 5-полевое cron-выражение
Enabled: переключатель вкл/выкл
```
#### Примеры cron-выражений
| Выражение | Значение |
|------------|---------|
| `0 2 * * *` | Каждый день в 02:00 |
| `0 3 * * 0` | Каждое воскресенье в 03:00 |
| `0 1 * * 1-5` | Будни в 01:00 |
| `30 4 1 * *` | Первый день каждого месяца в 04:30 |
#### Политики хранения
Настройте, сколько копий хранить автоматически:
| Параметр | Описание | По умолчанию |
|-----------|-------------|---------|
| `keep_last` | Хранить N последних копий | 3 |
| `keep_daily` | Одну в день в течение N дней | 7 |
| `keep_weekly` | Одну в неделю в течение N недель | 4 |
| `keep_monthly` | Одну в месяц в течение N месяцев | 6 |
#### Управление заданиями
- **List jobs** — все задания (админы видят все; пользователи — свои)
- **Enable/disable** — переключить без удаления
- **Edit** — изменить расписание, хранение, хранилище
- **Delete** — удалить задание и его запись в APScheduler
### Отслеживание задач
Каждая операция копирования (создание / восстановление / удаление) возвращает UPID Proxmox. Используйте его для опроса статуса и логов:
```bash
# Статус задачи
GET /api/backups/task/{server_id}/{node}/{upid}
```
---
## 🌐 IPAM
### Сети
Создавайте сети для организации IP-пространства:
```
Name: Production Network
CIDR: 192.168.1.0/24
VLAN: 100
Gateway: 192.168.1.1
DNS: 8.8.8.8, 8.8.4.4
Proxmox Server: выбрать сервер
Proxmox Node: выбрать узел (предотвращает назначение IP между серверами)
```
### Пулы IP
Пулы задают диапазоны для автоматического выделения:
```
Pool: Web Servers
Start: 192.168.1.10
End: 192.168.1.50
```
### Несколько адресов на инстанс
У гостя есть один **основной** адрес и любое количество дополнительных. Основной — это адрес из конфигурации интерфейса гостя (`net0` у LXC, `ipconfig0` у QEMU), именно он показывается в колонке IP списка инстансов. Дополнительные адреса навешиваются **внутри** гостя как alias на существующий интерфейс.
Управление — на вкладке **«Сеть»** инстанса, карточка **«IP-адреса»**:
| Действие | Что происходит |
|----------|----------------|
| Добавить IP | Выделяет адрес (следующий свободный в сети/пуле или введённый вручную) и применяет его к выбранному интерфейсу |
| Применить | Повторяет применение адреса, который раньше не удалось передать гостю |
| Сделать основным | Переносит флаг основного адреса; колонка IP и кэш инстанса следуют за ним |
| Освободить | Снимает адрес с гостя, затем освобождает его в IPAM |
Основной адрес из этой карточки не освобождается — он задаётся конфигурацией сетевого интерфейса и меняется через неё.
### Как применяется дополнительный адрес
Адрес поднимается командой `ip addr add`, сеть гостя при этом **не перезапускается**, поэтому работающий гость не теряет связь. Доставка использует тот же движок скриптов, что и раздел Scripts:
- **LXC** — `pct exec` по SSH к ноде (нужен пароль сервера либо SSH-ключ для `root@node`)
- **QEMU** — guest agent, в котором должен быть разрешён `guest-exec` (в некоторых образах он заблокирован в `/etc/sysconfig/qemu-ga` или `/etc/default/qemu-guest-agent`, см. [Установка Guest Agent](#установка-guest-agent); в образах встроенного каталога блокировка уже снята)
Proxmox пересоздаёт сетевую конфигурацию гостя при **каждом старте**, стирая всё, что было записано в родной стек. Чтобы адрес пережил перезагрузку, PVEmanager устанавливает небольшой oneshot systemd-юнит (`pvemanager-aliases.service`), который заново поднимает записанные адреса после `network-online.target`; каталог `/etc/systemd` Proxmox не трогает. Родной стек настраивается дополнительно, если он распознан — ifupdown, netplan, NetworkManager или sysconfig/network-scripts для RHEL-подобных гостей.
### Состояния адреса
| Состояние | Значение |
|-----------|----------|
| 🟢 applied | Поднят в госте и записан так, что вернётся после перезагрузки |
| 🟡 runtime_only | Поднят в госте, но сетевой стек не распознан и systemd нет — после перезагрузки исчезнет |
| 🟠 pending | Зарезервирован в IPAM, но гостю ещё не передан (гость выключен, нет guest agent, нет SSH к ноде) — нажмите **«Применить»** позже |
| 🔴 failed | Скрипт отработал с ошибкой; причина показана рядом с адресом |
Адрес резервируется в IPAM в любом случае, даже если применить его не удалось, — чтобы тем временем он не достался кому-то ещё.
### Статус IP
| Статус | Описание |
|--------|-------------|
| 🟢 Available | Свободен |
| 🔵 Allocated | Назначен ВМ |
| 🟠 Reserved | Зарезервирован |
---
## 💻 Удалённое выполнение команд
### Требования
- **QEMU Guest Agent** установлен и запущен в ВМ
- ВМ должна быть запущена
### Установка Guest Agent
**Debian/Ubuntu:**
```bash
apt install qemu-guest-agent
systemctl enable --now qemu-guest-agent
```
**CentOS/RHEL/AlmaLinux/Rocky/Fedora:**
```bash
dnf install qemu-guest-agent
systemctl enable --now qemu-guest-agent
```
В этих дистрибутивах агент по умолчанию запрещает `guest-exec` — панель получает `The command guest-exec has been disabled for this instance`, и выполнение команд, выдача дополнительных IP и расширение ФС не работают. Разблокировать:
```bash
sed -i -e 's/^FILTER_RPC_ARGS=.*/FILTER_RPC_ARGS=/' -e 's/^BLACKLIST_RPC=.*/BLACKLIST_RPC=/' -e 's/^BLOCK_RPCS=.*/BLOCK_RPCS=/' /etc/sysconfig/qemu-ga
systemctl restart qemu-guest-agent
```
Разметка `/etc/sysconfig/qemu-ga` менялась от версии к версии, поэтому команда чистит все три варианта: `FILTER_RPC_ARGS` (актуальные пакеты — allow-list, в котором `guest-exec` просто отсутствует), `BLOCK_RPCS` (qemu-ga 8.1+) и `BLACKLIST_RPC` (до 8.1). Пустое значение = агент без ограничений, как в Debian/Ubuntu, где фильтра нет вовсе; отсутствующие строки sed просто не найдёт.
**Windows:**
Скачайте [virtio-win drivers](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/)
### Использование
1. Откройте детали ВМ
2. Перейдите на вкладку **Commands**
3. Введите команду или выберите из быстрых команд
4. Нажмите **Execute**
### Быстрые команды
| Команда | Описание |
|---------|-------------|
| `df -h` | Использование диска |
| `free -h` | Использование памяти |
| `uptime` | Время работы |
| `ps aux` | Список процессов |
| `systemctl status` | Статус служб |
### Ограничения
- Таймаут: 30 секунд
- Интерактивные команды не поддерживаются
- Требуется Guest Agent
---
## 📊 Мониторинг
### Метрики в реальном времени
- **CPU**: суммарная нагрузка + по ядрам
- **RAM**: использовано/всего
- **Диск**: использовано/всего + I/O (карточки по каждому диску на вкладке Overview)
- **Сеть**: входящий/исходящий трафик (скорости в байт/с)
- **Disk IOPS**: скорости чтения/записи по каждому диску через QEMU guest agent (`get_vm_fsinfo`)
- **Скорости сетевого I/O**: живые байт/с, вычисляемые из накопительных счётчиков в `metrics_broadcaster`
#### Гранулярность данных
| Источник | Гранулярность | Используется для |
|--------|-------------|---------|
| `cluster/resources` (pvestatd) | 10 с | Обзор узлов и хранилищ |
| `status/current` | 1 с | Метрики запущенных ВМ в реальном времени |
Все запущенные ВМ опрашиваются через `status/current` с частотой 1 с при ограниченной конкурентности. Результаты транслируются по WebSocket.
#### Где отображаются метрики
| Расположение | Показываемые данные |
|----------|-----------|
| Инстанс → вкладка Overview | Карточки заполнения по дискам, карточка Network I/O, карточка Disk I/O-rate |
| Список инстансов | Столбцы Net I/O и Disk I/O; Disk% для LXC |
| Список top-loaded на дашборде | Оверлей метрик CPU, RAM, Disk, Net |
### Периоды графиков
- Час
- День
- Неделя
- Месяц
### Оповещения
Пороговые значения для уведомлений:
| Метрика | Warning | Critical |
|--------|---------|----------|
| CPU | 80% | 90% |
| RAM | 85% | 95% |
| Диск | 90% | 95% |
---
## 🔒 Безопасность
### Обзор
PVEmanager включает комплексные функции безопасности для защиты вашей инфраструктуры.
### RBAC v2 — контроль доступа на основе ролей
> **Это тот раздел, который стоит прочитать, если RBAC кажется запутанным.** Здесь модель разобрана от начала до конца: что такое право (permission), как реально проверяется запрос, что умеет каждая встроенная роль и — самое важное — как реализовано «пользователь видит только свои ВМ» (это **отдельный** механизм от RBAC, который легко настроить неправильно). Источники истины в бэкенде: `backend/app/rbac/permissions.py` (каталог прав), `backend/app/rbac/engine.py` (логика проверки), `backend/app/rbac/migration.py` (роли по умолчанию).
#### Ментальная модель в одном абзаце
У **пользователя** ровно одна **роль**. Роль — это просто именованная JSON-карта **прав** → `true`/`false` (напр., `{"vm:view": true, "vm:delete": false}`). Каждый защищённый эндпоинт API объявляет нужное ему право (напр., «нужно `vm:create`»). При каждом запросе **Permission Engine** смотрит на карту прав роли вызывающего и отвечает да/нет. Нет ни ACL для отдельных ресурсов, ни рабочих «scope» у прав — право либо выдано роли, либо нет. Единственное кажущееся исключение — «пользователи видят только свои инстансы» — вообще **не** является правом: это отдельный фильтр по владельцу, описанный в конце раздела.
#### Формат права
Каждое право — атомарная строка:
```
resource:action
```
- **`resource`** — *над чем* вы действуете (`vm`, `backup`, `user`, …).
- **`action`** — *что вы делаете* (`view`, `create`, `delete`, …).
Примеры: `vm:view` (видеть ВМ), `server:create` (добавить сервер Proxmox), `log:export` (скачать логи аудита), `role:manage` (полное администрирование ролей).
> **Про `:scope`.** Технически dataclass права поддерживает третий сегмент `:scope`, но **ни одно встроенное право его не использует** — все реальные права глобальные (`resource:action`). Игнорируйте scope при проектировании ролей; изоляция арендаторов реализована через владельца, а не через scope `:own`. (В старых версиях этой вики документировались вымышленные коды вроде `vms:view:own` — их никогда не было в коде, и они удалены.)
#### Как на самом деле работает проверка права (движок)
Когда эндпоинт спрашивает «есть ли у пользователя `X:Y`?», `PermissionEngine.has_permission` применяет правила **по порядку** и возвращает `true` на первом совпадении:
1. **Обход для админа.** Если пользователь — администратор (`is_admin` или роль с именем `admin`), ответ всегда **да** — админы неявно держат все права и никогда не блокируются.
2. **Прямая выдача.** Точный код присутствует и равен `true` в карте роли (`vm:create` есть в роли → да).
3. **Wildcard-выдача.** Роль держит `resource:*` (напр., `vm:*` даёт *все* действия `vm:`). Wildcard не используется встроенными ролями, но вы можете добавить его в кастомные роли.
4. **`manage` подразумевает частые действия.** Если роль держит `resource:manage`, движок также выдаёт эти действия для ресурса: **`view`, `list`, `update`, `start`, `stop`, `restart`**. Так `vm:manage` в одиночку позволяет смотреть/редактировать/управлять питанием ВМ, не перечисляя эти действия отдельно. ⚠️ Учтите: `manage` **не** подразумевает `create` или `delete` — выдавайте их явно.
Если ни одно правило не совпало, доступ отклоняется с **HTTP 403 `Permission denied: X:Y`**.
> **Точки применения.** Эндпоинты защищают доступ через FastAPI-зависимость `PermissionChecker` (`Depends(PermissionChecker("vm:create"))`) или inline-вызов `require_permission(user, "vm:create")`. Фронтенд независимо скрывает пункты меню, на которые у пользователя нет прав (см. таблицу соответствия ниже), но реальной границей безопасности является именно проверка на бэкенде.
#### `requires` — зависимости прав
Многие права объявляют предпосылки (поле `requires` в `permissions.py`). Они выражают логические зависимости — напр., `vm:create` `requires` `vm:view` + `template:view`; `vm:execute` `requires` `vm:console`.
> **Важно:** `requires` — это **документация / подсказка для UI**, а **не** проверка в рантайме — движок не выдаёт предпосылки автоматически. Собирая кастомную роль, отмечайте предпосылки сами, иначе функция может работать наполовину (напр., пользователь, который может `vm:create`, но не имеет `vm:view`, не увидит только что созданную ВМ).
#### Полный каталог прав
Сгруппировано по ресурсам. «Подразумевается `manage`» означает, что движок выдаёт это бесплатно, когда роль держит `<resource>:manage` (правило 4 выше).
**Дашборд**
| Право | Что открывает |
|---|---|
| `dashboard:view` | Доступ к странице Дашборда и обзорной статистике |
**Серверы Proxmox и кластер** (`server`, `cluster`)
| Право | Что открывает |
|---|---|
| `server:view` | Видеть список серверов/узлов и статус |
| `server:create` | Добавить новый сервер Proxmox |
| `server:update` | Редактировать параметры подключения сервера |
| `server:delete` | Удалить сервер Proxmox |
| `server:manage` | Полное управление сервером, включая кластерные операции (требует `server:view`) |
| `cluster:manage` | Создание кластеров, присоединение/исключение узлов (требует `server:view`) |
**Виртуальные машины** (`vm`)
| Право | Что открывает |
|---|---|
| `vm:view` | Видеть ВМ (с учётом фильтра владельца — см. ниже) |
| `vm:create` | Создавать ВМ (требует `vm:view`, `template:view`) |
| `vm:update` | Менять конфигурацию ВМ (CPU/RAM/диск) |
| `vm:delete` | Удалять ВМ |
| `vm:start` / `vm:stop` / `vm:restart` | Действия питания |
| `vm:console` | Консоль noVNC / xterm.js |
| `vm:migrate` | Миграция между узлами одного кластера |
| `vm:remote_migrate` | Миграция в *другой* зарегистрированный кластер (требует `vm:migrate`) |
| `vm:execute` | Выполнять команды через QEMU guest agent (требует `vm:console`) |
| `vm:manage` | Полное управление ВМ **и глобальный флаг «видеть все инстансы»** (см. раздел про изоляцию) |
**LXC-контейнеры** (`lxc`) — та же структура, что у VM: `view`, `create`, `update`, `delete`, `start`, `stop`, `restart`, `console`, `migrate`, `remote_migrate`. (Отдельного `lxc:manage` нет; листинг/питание контейнеров управляется действиями `lxc:*` и тем же фильтром владельца, что и ВМ.)
**Шаблоны и образы** (`template`)
| Право | Что открывает |
|---|---|
| `template:view` | Просматривать шаблоны ОС |
| `template:create` / `template:update` / `template:delete` | Управлять записями шаблонов |
| `template:manage` | Полное управление шаблонами, включая страницу **Images** (скачивание облачных образов) |
**Хранилище** (`storage`): `storage:view`, `storage:manage`.
**Резервные копии** (`backup`): `backup:view`, `backup:create`, `backup:restore`, `backup:delete`, `backup:manage` (хранилища + запланированные задания).
**IPAM** (`ipam`): `ipam:view`, `ipam:manage`.
**Сети** (`network`): `network:view`, `network:manage` (интерфейсы узла + зоны/vnet/подсети SDN).
**Firewall** (`firewall`): `firewall:view`, `firewall:manage` (правила датацентра/узла/ВМ, группы, IP-наборы).
**Администрирование узла** (`node`): `node:view`, `node:manage` (службы systemd, APT), `node:upgrade` (apt dist-upgrade), `node:power` (перезагрузка/выключение физического узла).
**Пулы ресурсов** (`pool`): `pool:view`, `pool:manage`.
**PVE-доступ** (`access`): `access:view`, `access:manage` (realm-ы аутентификации и API-токены Proxmox).
**App Store** (`app`): `app:view` (каталог + Мои приложения), `app:install`, `app:manage` (синхронизация, обновление, откат, удаление).
**Скрипты** (`script`): `script:view`, `script:execute`, `script:manage` (редактирование скриптов и git-источников).
**Пользователи, квоты и роли** (`user`, `quota`, `role`)
| Право | Что открывает |
|---|---|
| `user:view` | Видеть список пользователей |
| `user:create` / `user:update` / `user:delete` | Управлять учётными записями |
| `user:manage` | Полное администрирование пользователей, включая управление SSH-ключами **других** пользователей |
| `quota:view` / `quota:manage` | Читать / задавать квоты ресурсов пользователей (требует `user:view`) |
| `role:view` | Видеть роли и их права |
| `role:create` / `role:update` / `role:delete` | Управлять ролями |
| `role:manage` | Полное администрирование ролей, включая назначение (требует `role:view`, `user:view`) |
**Логи** (`log`): `log:view`, `log:export`, `log:delete`.
**Настройки** (`setting`): `setting:view`, `setting:update` (настройки панели), `setting:manage` (безопасность и расширенные настройки). Эти права относятся **только к настройкам панели** — собственная учётная запись пользователя (профиль, пароль, 2FA, SSH-ключи, предпочтения уведомлений) живёт на `/profile`, ограничена самим пользователем и не требует никаких прав.
**Уведомления** (`notification`): `notification:view`, `notification:manage`.
> **Делегированное редактирование ролей.** Не-админ с `role:manage` может выдавать только те права, которыми **владеет сам** (`can_assign_permission` / `validate_role_permissions`). Это предотвращает эскалацию привилегий — нельзя раздать доступ, которого у вас нет. Админы освобождены от этого и могут выдавать что угодно.
#### Какое право открывает какую страницу в меню
Фронтенд (`frontend/src/lib/nav-items.ts`) скрывает пункт меню, если пользователь не админ и не держит указанное право. Пункты **без** права — **только для админа**. То же право защищает и сам маршрут (`RequirePermission` в `frontend/src/components/shared/route-guards.tsx`), поэтому скрытую страницу нельзя открыть вводом URL — при отказе пользователя перебрасывает на первую доступную ему страницу.
| Пункт меню | Требуемое право |
|---|---|
| Dashboard | `dashboard:view` |
| Instances | `vm:view` |
| Nodes | `server:view` |
| Cluster | `cluster:manage` |
| Templates | `template:view` |
| Images | `template:manage` |
| Backups | `backup:view` |
| Tasks | `vm:view` |
| App Store / My Apps | `app:view` |
| Scripts | `script:view` |
| IPAM | `ipam:view` |
| Networks | `server:manage` |
| Users | `user:view` |
| Workspaces | *(только админ)* |
| Logs | `log:view` |
| Settings (настройки панели) | `setting:view` |
| Профиль (меню пользователя, не сайдбар) | *(без права — любой вошедший)* |
#### Роли по умолчанию (встроенные)
Четыре роли пересоздаются/обновляются при каждом старте функцией `ensure_default_roles_new_format`. Это системные (`is_system`) роли. Логика посева **только добавляет отсутствующие ключи прав** — она никогда не перезаписывает ключи, которые админ уже переключил в UI, — поэтому ваши правки этих ролей переживают обновления. Новые права, добавленные при обновлении версии, также **добавляются (backfill)** к кастомным ролям, которые уже держат соответствующее «зонтичное» право (`backfill_granular_permissions`), поэтому кастомная роль на базе `server:manage` автоматически получает новые ключи `node:*` / `network:*` вместо молчаливой потери доступа.
| Роль | Назначение | Что может делать |
|------|--------------|----------------|
| **admin** | Администратор платформы | Всё. Держит каждое право *и* обход `is_admin` — видит все серверы, все инстансы (независимо от владельца), всех пользователей, настройки, безопасность. |
| **moderator** | Оператор без прав админа | Смотреть дашборд/серверы; **создавать + управлять питанием + консоль** ВМ и LXC; смотреть шаблоны/хранилище/firewall/сеть/узел/пулы/копии/IPAM/пользователей/логи; создавать копии; экспортировать логи; управлять уведомлениями. **Нет** удаления, изменения настроек, управления пользователями/ролями. Заметьте: без `vm:manage` модератор всё равно подчиняется фильтру владельца (см. ниже). |
| **user** | Стандартный арендатор (стиль VPS) | Смотреть + **start/stop/restart/console** для **своих** ВМ и LXC; смотреть шаблоны/хранилище/firewall/сеть/узел/пулы/IPAM; управлять своими уведомлениями. Собственный профиль, SSH-ключи и квота живут на `/profile` и не требуют прав, поэтому у роли намеренно **нет** `setting:view` (он открыл бы страницу настроек панели). **Нет** create/delete по умолчанию, **нет** дашборда. Ограничен своими инстансами, так как не имеет `vm:manage`. |
| **demo** | Read-only витрина | Только просмотр: дашборд, серверы, ВМ, LXC, шаблоны, хранилище, firewall, сеть, узел, пулы, IPAM. Ни действий питания, ни записи. |
> Точный JSON каждой роли лежит в `NEW_DEFAULT_ROLES` в `backend/app/rbac/migration.py` — сверяйтесь с ним, если нужен побайтовый набор прав.
#### Управление ролями в UI
1. Откройте **Пользователи** (нужно `role:view`; для редактирования — `role:manage` или админ).
2. Перейдите на вкладку **Roles** → **New Role** или отредактируйте существующую.
3. Права сгруппированы по категориям (Virtual Machines, Backups, User Management, …). Отметьте нужные чекбоксы.
4. Не забудьте отметить и **предпосылки** (`requires`) — движок не включает их автоматически.
5. Назначьте роль пользователю в диалоге редактирования пользователя.
Системные роли (admin/moderator/user/demo) удалить нельзя, но их наборы прав можно настраивать (ваши изменения сохраняются при обновлениях).
#### Legacy-формат прав
Старые релизы использовали точечный формат (`vms.view`, `proxmox.manage`). Он отображается на новый двоеточный формат через `LEGACY_PERMISSION_MAP` и конвертируется **один раз** в базе данных при старте, поэтому трансляция в рантайме не нужна. Вы всё ещё можете вписать legacy-код в кастомную роль — он разрешается прозрачно, — но новые роли должны использовать форму `resource:action`.
---
### Изоляция пользователей — «каждый видит только свои инстансы»
Это функция, которую чаще всего путают с RBAC. **Это не право.** Это отдельный фильтр по владельцу, наложенный *поверх* прав `vm:*`/`lxc:*`. Понимание этого разделения — ключ к правильной настройке арендаторов.
#### Правило
| Вызывающий | Видит / управляет |
|---|---|
| Админ или роль с именем `admin` | **Всеми** инстансами |
| Любая роль, держащая **`vm:manage`** | **Всеми** инстансами |
| Все остальные (включая стоковую роль `user`) | **Только** инстансами, которыми **владеет** (`owner_id == user.id`) |
Решение принимает `can_view_all_instances` в `backend/app/api/proxmox/_helpers.py`. Обратите внимание: ключ «видеть всё» — это **`vm:manage`**, а **не** `vm:view`. Стоковая роль `user` намеренно несёт только `vm:view`, чтобы страница Instances вообще работала — `vm:view` сам по себе никогда не раскрывает ВМ других арендаторов.
> ⚠️ **Самая частая ошибка конфигурации:** выдать `vm:manage` роли `user` (или кастомной «арендаторской» роли), чтобы «дать им управлять своими ВМ». Это включает глобальный обзор, и каждый пользователь внезапно видит **все** ВМ в панели. Для арендаторов выдавайте гранулярные действия (`vm:update`, `vm:delete`, `vm:start`, …) вместо `vm:manage`. Резервируйте `vm:manage` для операторов/админов, которые *должны* видеть всё.
#### Как владение применяется везде
Владение проверяется не только в списке инстансов, но и на каждой поверхности, доступной непривилегированному пользователю:
- **Список инстансов и действия над одним инстансом** — `check_vm_access` / `require_vm_access` проверяют `owner_id` перед любой операцией view/питание/консоль/снимок/копия/удаление.
- **Эндпоинты живых ресурсов** — дашборд (`/api/resources/all`) и страница узла/сервера (`/api/{server_id}/resources`) вызывают `get_owned_vmids` и фильтруют живой листинг Proxmox до принадлежащих VMID, поэтому пользователь только с `server:view` не может перечислить ВМ других владельцев через страницу узла.
#### Назначение владельца
1. **Автоматически** — когда пользователь развёртывает VM/LXC, он становится её `owner_id`.
2. **Вручную (админ)** — откройте инстанс → кнопка **Owner** → выберите пользователя → Save. REST API см. в [Владелец ВМ / LXC](#-владелец-вм--lxc).
Инстансы, синхронизированные из Proxmox до появления владения, имеют `owner_id = NULL` (видны только привилегированным пользователям). Массово назначить им владельца:
```bash
docker compose exec app python assign_instances_owner.py
```
#### RBAC vs Workspaces vs назначение серверов vs владение
Эти четыре механизма складываются — не-админ должен пройти **все** применимые фильтры:
| Слой | На какой вопрос отвечает | Где настраивается |
|---|---|---|
| **Роль RBAC** | *Какие действия* пользователь вообще может выполнять? | Пользователи → Roles |
| **Workspaces** | *Какие серверы* пользователю разрешено видеть? | [Рабочие области](#-рабочие-области-workspaces) |
| **Назначение серверов** | *Какие конкретные серверы* внутри этих областей? | [Назначение серверов пользователю](#-назначение-серверов-пользователю) |
| **Владение** | *Какие отдельные ВМ/LXC* (если нет `vm:manage`/админа)? | [Владелец ВМ / LXC](#-владелец-вм--lxc) |
---
### Снимки (в контексте безопасности)
Снимки позволяют сохранять и восстанавливать состояние ВМ и контейнеров.
#### Снимки ВМ
```bash
# Список снимков
GET /api/{server_id}/vm/{vmid}/snapshots?node={node}
# Создать снимок
POST /api/{server_id}/vm/{vmid}/snapshots?node={node}
{
"snapname": "before-update",
"description": "Before system update",
"vmstate": false # Включить состояние RAM (опционально)
}
# Удалить снимок
DELETE /api/{server_id}/vm/{vmid}/snapshots/{snapname}?node={node}
# Откатить к снимку
POST /api/{server_id}/vm/{vmid}/snapshots/{snapname}/rollback?node={node}&start=false
```
#### Снимки контейнеров
Те же эндпоинты, но используйте `/container/` вместо `/vm/`.
### SDN (Software Defined Networking)
Управление зонами SDN и виртуальными сетями Proxmox.
#### Проверка доступности SDN
```bash
GET /api/servers/{server_id}/sdn/status
```
Возвращает:
```json
{
"server_id": 1,
"sdn_available": true,
"pending_changes": false
}
```
#### Типы зон
| Тип | Описание |
|------|-------------|
| `simple` | Базовая изолированная сеть |
| `vlan` | Разделение на основе VLAN |
| `vxlan` | Overlay-сеть VXLAN |
| `evpn` | EVPN/VXLAN с BGP |
#### Эндпоинты SDN API
```bash
# Список зон
GET /api/servers/{server_id}/sdn/zones
# Создать зону
POST /api/servers/{server_id}/sdn/zones
{
"zone": "myzone",
"type": "simple"
}
# Обновить зону
PUT /api/servers/{server_id}/sdn/zones/{zone}
{
"mtu": 1500,
"dns": "8.8.8.8"
}
# Список VNet
GET /api/servers/{server_id}/sdn/vnets
# Создать VNet
POST /api/servers/{server_id}/sdn/vnets
{
"vnet": "vnet1",
"zone": "myzone",
"alias": "Production Network"
}
# Обновить VNet
PUT /api/servers/{server_id}/sdn/vnets/{vnet}
{
"alias": "Updated Alias",
"tag": 100
}
# Создать подсеть (с опциональным авто-созданием IPAM)
POST /api/servers/{server_id}/sdn/vnets/{vnet}/subnets
{
"subnet": "10.0.0.0/24",
"gateway": "10.0.0.1",
"snat": true,
"create_ipam_network": true
}
# Удалить подсеть (опционально удалить связанную сеть IPAM)
DELETE /api/servers/{server_id}/sdn/vnets/{vnet}/subnets/{subnet_cidr}?delete_ipam_network=true
# Применить изменения (обязательно после модификаций)
POST /api/servers/{server_id}/sdn/apply
```
### Сетевые интерфейсы узлов
Управление сетевыми интерфейсами (мосты, bond-ы, VLAN) напрямую на узлах Proxmox.
#### Доступ к странице «Сети»
1. Откройте **Networks** из бокового меню (требует право `server:manage`; записи в SDN/интерфейсы требуют `network:manage`)
2. Выберите **сервер** из выпадающего списка
3. Страница показывает две вкладки: **SDN** и **Node Interfaces**
#### Управление интерфейсами узла
| Действие | Описание |
|--------|-------------|
| List interfaces | Просмотр всех интерфейсов на выбранном узле |
| Create interface | Создать мост, bond, VLAN или alias |
| Edit interface | Изменить IP, маску, шлюз, порты и т.д. |
| Delete interface | Удалить конфигурацию интерфейса |
| Apply config | Активировать ожидающие изменения |
| Revert config | Откатить к рабочей конфигурации |
> ⚠️ Всегда **применяйте** изменения после создания или редактирования интерфейсов. Изменения находятся в staging до применения.
#### Эндпоинты Node Network API
```bash
# Список узлов сервера
GET /api/servers/{server_id}/nodes
# Список интерфейсов узла
GET /proxmox/api/servers/{server_id}/nodes/{node}/networks
# Детали интерфейса
GET /proxmox/api/servers/{server_id}/nodes/{node}/networks/{iface}
# Создать интерфейс
POST /proxmox/api/servers/{server_id}/nodes/{node}/networks
{
"iface": "vmbr1",
"type": "bridge",
"address": "10.0.0.1",
"netmask": "255.255.255.0",
"bridge_ports": "ens4",
"autostart": true
}
# Обновить интерфейс
PUT /proxmox/api/servers/{server_id}/nodes/{node}/networks/{iface}
{
"address": "10.0.0.2"
}
# Удалить интерфейс
DELETE /proxmox/api/servers/{server_id}/nodes/{node}/networks/{iface}
# Применить ожидающую конфигурацию сети
PUT /proxmox/api/servers/{server_id}/nodes/{node}/networks/apply
# Откатить ожидающие изменения
DELETE /proxmox/api/servers/{server_id}/nodes/{node}/networks/revert
```
#### Совместимость со старым форматом
Старый формат прав (`vms.view`, `proxmox.manage`) по-прежнему поддерживается и автоматически конвертируется в новый формат.
### Блокировка IP
Автоматическая и ручная блокировка IP защищает от brute-force атак.
#### Автоматическая блокировка
Когда пользователь несколько раз не может войти с одного IP:
1. После порога (по умолчанию: 10) неудачных попыток
2. IP блокируется на настроенное время (по умолчанию: 60 минут)
3. Блокировка может быть временной или постоянной
#### Ручная блокировка
Администраторы могут вручную блокировать IP:
- Блокировка с указанием причины
- Задать длительность или сделать постоянной
- Просмотр списка заблокированных IP
- Разблокировка по мере необходимости
### Управление сессиями
Отслеживание и контроль активных пользовательских сессий.
| Функция | Описание |
|---------|-------------|
| Active Sessions | Просмотр всех вошедших пользователей |
| Device Info | Определение браузера и устройства |
| IP Tracking | Логирование IP-адреса сессии |
| Single Session | Опция разрешить только одну сессию на пользователя |
| Force Logout | Завершить любую сессию |
### Защита входа
| Настройка | По умолчанию | Описание |
|---------|---------|-------------|
| Max Login Attempts | 5 | Попыток до блокировки аккаунта |
| Lockout Duration | 30 мин | Как долго аккаунт заблокирован |
| IP Block Threshold | 10 | Неудачных попыток до блокировки IP |
| IP Block Duration | 60 мин | Как долго IP заблокирован |
### Настройки безопасности
Доступ через **Настройки → Безопасность**:
| Настройка | По умолчанию | Описание |
|---------|---------|-------------|
| Session Timeout | 60 мин | Авто-выход после бездействия |
| Single Session | Выкл | Разрешить только одну сессию на пользователя |
| Password Min Length | 8 | Минимум символов пароля |
| Require Uppercase | Да | Пароль должен содержать заглавные |
| Require Lowercase | Да | Пароль должен содержать строчные |
| Require Numbers | Да | Пароль должен содержать цифры |
| Require Special | Нет | Пароль должен содержать спецсимволы |
| Password Expiry | 0 (никогда) | Дней до истечения пароля |
| API Rate Limit | 60/мин | Запросов API в минуту |
### Лучшие практики
1. **Смените пароли по умолчанию** сразу после установки
2. **Включите SSL/HTTPS** в продакшне
3. **Используйте надёжные пароли** с требованиями к сложности
4. **Мониторьте попытки входа** на предмет подозрительной активности
5. **Настройте файрвол** для ограничения доступа к панели
6. **Регулярно обновляйтесь** для получения патчей безопасности
---
## 📝 Логи и аудит
### Обзор
Страница **Логи** (`/logs` в боковом меню) даёт админам полный поисковый обзор таблицы `audit_logs` панели. Она дополняет ленту активности дашборда более глубокой фильтрацией, пагинацией и агрегированной статистикой за 24 часа.
### Макет страницы
1. **Карточки статистики (окно 24ч)** — всего событий, число ошибок, неудачные входы, число строк на текущей странице
2. **Фильтры** — свободный поиск (по сообщению и имени пользователя) и выпадающий список уровней из `GET /logs/api/levels`
3. **Таблица логов** — время, бейдж уровня, категория, имя пользователя, сообщение
4. **Пагинация** — управляется API (`page`, `limit`)
### Уровни логов
| Уровень | Цвет | Описание |
|-------|-------|-------------|
| `INFO` | 🔵 Синий | Информационные события |
| `SUCCESS` | 🟢 Зелёный | Успешные операции |
| `WARNING` | 🟡 Жёлтый | Некритические предупреждения |
| `ERROR` | 🔴 Красный | Сбои |
| `CRITICAL` | 🟣 Фиолетовый | Критические события безопасности / системы |
### Справочник API
| Метод | Эндпоинт | Описание |
|--------|----------|-------------|
| `GET` | `/logs/api/logs` | Список записей аудита (с пагинацией) |
| `GET` | `/logs/api/stats` | Агрегированная статистика (по умолчанию 24ч) |
| `GET` | `/logs/api/levels` | Различные уровни для выпадающего фильтра |
| `GET` | `/logs/api/categories` | Различные категории |
#### Параметры запроса для `GET /logs/api/logs`
| Имя | Тип | Описание |
|------|------|-------------|
| `search` | string | Свободный поиск по сообщению/имени пользователя |
| `level` | string | Фильтр по уровню (`INFO`, `ERROR`, …) |
| `category` | string | Фильтр по категории |
| `username` | string | Фильтр по имени пользователя |
| `page` | int | Номер страницы (с 1) |
| `limit` | int | Размер страницы (по умолчанию 50) |
| `date_from` | ISO date | Нижняя граница (включительно) |
| `date_to` | ISO date | Верхняя граница (включительно) |
#### Форма ответа
```json
{
"logs": [
{
"id": 1234,
"timestamp": "2026-06-04T10:21:33Z",
"level": "INFO",
"category": "vm",
"username": "admin",
"message": "VM 100 started on node pve1"
}
],
"total": 532,
"page": 1,
"limit": 50,
"pages": 11
}
```
### Контроль доступа
Страница «Логи» защищена правом `log:view` в RBAC v2. Стоковая роль `admin` имеет его; `moderator` имеет доступ только на чтение; роли `user` и `demo` эту страницу не видят.
### Хранение
Хранение логов управляется через **Настройки → Панель → Дни хранения логов** (добавлено в v1.5.2). Записи старше настроенного числа дней удаляются автоматически. По умолчанию — 30 дней.
---
## 🔑 Управление SSH-ключами
### Обзор
У каждого пользователя есть личная библиотека SSH-ключей. Ключи из библиотеки можно выбирать при развёртывании ВМ или LXC-контейнеров, избавляя от необходимости каждый раз вставлять публичный ключ вручную.
### Управление SSH-ключами
1. Перейдите в **Настройки** → **SSH Keys**
2. Нажмите **Add Key**
3. Заполните:
- **Name** — читаемая метка (напр., `Work laptop`)
- **Public Key** — вставьте полный публичный ключ (`ssh-rsa ...`, `ssh-ed25519 ...` и т.д.)
- **Private Key** *(опционально)* — хранится в базе в зашифрованном виде
- **Comment** *(опционально)* — свободная заметка
4. Нажмите **Save**
SHA-256 отпечаток вычисляется и отображается автоматически.
### Использование ключей при развёртывании
- В **мастере создания инстанса** на шаге **Config** показан селектор ключей
- Обычные пользователи видят свои ключи
- Админы, выбирающие владельца, видят ключи владельца в дополнение к своим
### Синхронизация SSH-ключей при смене владельца
Когда администратор переназначает ВМ или LXC другому пользователю (через диалог **Владелец** на странице инстанса), панель автоматически:
1. Удаляет публичный SSH-ключ предыдущего владельца из `ciuser`/authorized keys инстанса
2. Внедряет публичный SSH-ключ нового владельца
Учётные данные для доступа всегда соответствуют текущему владельцу — ручная ротация ключей не нужна.
### Управление ключами админом
Админы (пользователи с правом `user:manage`) могут просматривать и управлять SSH-ключами любого пользователя:
```
GET /api/ssh-keys/user/{user_id} — список ключей конкретного пользователя
```
### Справочник API
| Метод | Эндпоинт | Описание |
|--------|----------|-------------|
| `GET` | `/api/ssh-keys` | Список своих SSH-ключей |
| `POST` | `/api/ssh-keys` | Добавить новый ключ |
| `PUT` | `/api/ssh-keys/{id}` | Обновить ключ |
| `DELETE` | `/api/ssh-keys/{id}` | Удалить ключ |
| `GET` | `/api/ssh-keys/user/{user_id}` | Список ключей любого пользователя (админ) |
---
## 🌍 Локализация
### Поддерживаемые языки
- 🇷🇺 Русский (полное покрытие начиная с v1.16.0)
- 🇺🇸 Английский (по умолчанию)
- 🇺🇿 Узбекский (частично)
> **v1.16.0** завершил полный проход по переводам: все ранее захардкоженные или непереведённые строки на дашборде, в инстансах, образах, App Store, нодах, ролях, хранилищах, скриптах, сертификатах, графиках, мастере, массовой миграции и реалмах доступа теперь корректно переведены на русский и английский.
### Переключение языка
Начиная с v1.5.2, язык является **пользовательской** настройкой, хранящейся в базе данных.
1. **Настройки** → **Profile**
2. Выберите язык в выпадающем списке **Language**
3. Нажмите **Save** — интерфейс переключится немедленно без перезагрузки страницы
### Добавление нового языка
Переводы хранятся плоскими JSON-файлами в `backend/app/locales/`.
Чтобы добавить новый язык, просто создайте новый файл — изменения кода не требуются:
```bash
# Создать файл локали для узбекского
cp backend/app/locales/en.json backend/app/locales/uz.json
# Затем переведите значения в uz.json
```
Имя файла становится кодом языка (`uz.json` → `"uz"`). Панель подхватит его при следующем старте (или вызовите `I18nService.reload()` в рантайме).
> **Структура:** каждый файл — плоский JSON-объект `{"key": "переведённая строка"}`.
> Ключи должны совпадать с существующими ключами в `ru.json` / `en.json`.
---
## ⚙️ Настройки
### Профиль пользователя
- Полное имя
- Email (для уведомлений)
- Смена пароля
- **Язык** — язык UI пользователя (хранится в колонке БД `users.language`); применяется сразу при сохранении через `i18n.changeLanguage()`; у каждой учётной записи своя настройка
- **Публичный SSH-ключ** — хранится в профиле пользователя и автоматически внедряется в VM/LXC при развёртывании через cloud-init
### Настройки панели
| Параметр | Описание | По умолчанию |
|-----------|-------------|---------|
| Refresh interval | Частота обновления данных | 30 сек |
| Log retention | Сколько дней хранить логи | 30 дней |
| Panel name | Отображаемое имя (read-only в UI, можно переопределить в БД) | env `PANEL_NAME` |
> **Примечание:** настройка языка перенесена с вкладки «Панель» на вкладку «Профиль» в v1.5.2 — теперь каждый пользователь управляет своим языком независимо.
### Обновления системы и выбор репозитория
Панель поддерживает автоматические обновления из Git-репозитория. Можно выбрать, какой репозиторий использовать:
**Доступные варианты:**
1. **git.tzim.uz/markmorado/pvemanager** (по умолчанию) — основной репозиторий
2. **github.com/markmorado/pvemanager** — зеркало
3. **Кастомный репозиторий** — ваш форк или зеркало
**Как сменить репозиторий:**
1. Перейдите в **Настройки** → **System Updates**
2. В выпадающем списке «Git Repository» выберите предпочтительный репозиторий
3. Для кастомного репозитория:
- Выберите «Custom Repository...» из списка
- Введите полный URL (напр., `https://git.example.com/user/repo`)
- Нажмите Save
Настройка репозитория влияет на:
- Проверку обновлений (сравнение версий)
- Получение changelog
- Процесс обновления панели
> **Примечание:** убедитесь, что выбранный репозиторий содержит валидные файлы `VERSION` и `CHANGELOG.md`.
### Как работает процесс обновления
Начиная с v1.1.1, обновления выполняются **сторожевым процессом на хосте** (`pvemanager-update.service`), а не внутри контейнера. Это решает фундаментальную проблему, когда `docker compose down` убивал бы внутриконтейнерный скрипт до завершения пересборки.
```
[UI: "Update Panel"] → perform_update() → пишет .update_trigger
↓ (опрос каждые 3 сек)
[pvemanager-update.service на ХОСТЕ]
обнаруживает триггер → удаляет его → выполняет:
git pull
docker compose down
docker compose build --no-cache app
docker compose up -d
```
**Логи:** `./logs/update_host.log` (видны из панели)
### Установка сторожа обновлений
Сторож нужно установить один раз на хост-машине. Он автоматически настраивается при запуске `deploy.sh`, либо его можно установить/переустановить отдельно:
```bash
# Установка или переустановка (требует root для systemd)
sudo ./deploy.sh --watchdog
# Проверить, что запущен
systemctl status pvemanager-update
# Смотреть живой лог обновления
tail -f logs/update_host.log
```
**Требования:**
- systemd на хосте
- Пользователь, запускающий `deploy.sh --watchdog` (или `$SUDO_USER`), должен быть в группе `docker` (root разрешён всегда)
- Volume `.:/project:rw` в `compose.yml` должен присутствовать (он есть по умолчанию)
> **Примечание для non-root:** если вы запустите `./deploy.sh --watchdog` без sudo, он сгенерирует готовые файлы в `./systemd/` и напечатает точные `sudo`-команды для их установки.
### Настройки уведомлений
- In-App: всегда включены
- Email: требует настройки SMTP
- Telegram: требует токен бота
---
## 🛠️ Инструмент pve CLI
Начиная с v1.1.2, PVEmanager поставляется со скриптом `pve` — удобным CLI-ярлыком для частых задач управления.
### Установка
```bash
# Требует root (копирует скрипт в /usr/local/bin/pve и правит путь проекта)
sudo ./deploy.sh --install-cli
```
После установки команда `pve` доступна системно.
### Использование
```bash
pve help # Показать все доступные команды
pve logs # Смотреть живые логи app
pve restart # Перезапустить все контейнеры
pve update # Запустить обновление панели (через сторож)
pve exec <cmd> # Выполнить команду внутри контейнера app
pve status # Показать статус docker compose
pve dev # Запустить frontend в dev-режиме (Vite HMR)
pve dev stop # Остановить dev-режим, вернуться к production frontend
```
### Как это работает
- `deploy.sh --install-cli` читает файл `pve` из корня проекта
- Заменяет плейсхолдер `PVE_DIR` фактическим путём проекта
- Записывает результат в `/usr/local/bin/pve` и делает исполняемым
- Перезапустите `sudo ./deploy.sh --install-cli` после перемещения каталога проекта
> **Примечание:** бинарник `pve` отражает каталог проекта на момент установки. Если вы перемещаете проект — переустановите CLI.
---
## 🔌 Справочник API
### Аутентификация
```bash
# Получить токен
POST /api/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "admin123"
}
# Ответ
{
"access_token": "eyJ...",
"token_type": "bearer"
}
```
### Использование токена
```bash
curl -H "Authorization: Bearer eyJ..." \
http://localhost:8000/api/notifications
```
### Основные эндпоинты
| Метод | Эндпоинт | Описание |
|--------|----------|-------------|
| GET | `/api/servers` | Список серверов Proxmox |
| GET | `/api/servers/{id}` | Получить конкретный сервер |
| POST | `/api/servers` | Добавить сервер |
| PUT | `/api/servers/{id}` | Обновить сервер |
| DELETE | `/api/servers/{id}` | Удалить сервер |
| POST | `/api/servers/{id}/test` | Проверить подключение |
| GET | `/api/servers/{id}/resources` | ВМ и контейнеры сервера |
| POST | `/api/servers/{id}/vm/{vmid}/{action}` | Управление ВМ (start/stop/restart) |
| POST | `/api/servers/{id}/container/{vmid}/{action}` | Управление контейнером (start/stop/restart) |
| GET | `/api/servers/{id}/vm/{vmid}/status` | Статус ВМ |
| GET | `/api/servers/{id}/container/{vmid}/status` | Статус контейнера |
| GET | `/ipam/api/networks` | Список сетей IPAM |
| POST | `/ipam/api/networks` | Создать сеть IPAM |
| GET | `/ipam/api/allocations` | Список выделений IP |
| POST | `/ipam/api/allocations` | Создать выделение IP |
| GET | `/templates/api/groups` | Список групп шаблонов |
| GET | `/templates/api/templates` | Список шаблонов ОС |
| POST | `/templates/api/templates` | Создать шаблон ОС |
| GET | `/api/notifications` | Список уведомлений |
| PATCH | `/api/notifications/{id}/read` | Отметить прочитанным |
| DELETE | `/api/notifications/{id}` | Удалить уведомление |
| GET | `/api/users` | Список пользователей (только админ) |
| POST | `/api/users` | Создать пользователя (только админ) |
| GET | `/settings/api/panel` | Получить настройки панели |
| PUT | `/settings/api/panel` | Обновить настройки панели |
| GET | `/logs/api/logs` | Получить логи аудита |
| GET | `/logs/api/stats` | Статистика логов |
| GET | `/api/backups/storages/{server_id}` | Список хранилищ Proxmox |
| GET | `/api/backups/scan-nfs/{server_id}` | Сканировать NFS-сервер на экспорты |
| POST | `/api/backups/create` | Создать копию VM/LXC |
| POST | `/api/backups/restore` | Восстановить VM/LXC из копии |
| GET | `/api/backups/list/{server_id}` | Список файлов копий |
| DELETE | `/api/backups/{server_id}/backup` | Удалить файл копии |
| GET | `/api/backups/jobs` | Список запланированных заданий |
| POST | `/api/backups/jobs` | Создать запланированное задание |
| GET | `/api/{server_id}/ha/status` | Статус HA для всех ВМ |
| POST | `/api/{server_id}/ha/{vm_type}/{vmid}/add` | Добавить ВМ в HA-группу |
| DELETE | `/api/{server_id}/ha/{vm_type}/{vmid}/remove` | Убрать ВМ из HA |
| POST | `/api/sync-vms` | Принудительная синхронизация ВМ из Proxmox |
| GET | `/api/servers/{id}/nodes` | Список узлов сервера |
| PUT | `/api/servers/{id}/sdn/zones/{zone}` | Обновить зону SDN |
| PUT | `/api/servers/{id}/sdn/vnets/{vnet}` | Обновить VNet SDN |
| DELETE | `/api/servers/{id}/sdn/vnets/{vnet}/subnets/{cidr}` | Удалить подсеть SDN |
### Документация Swagger
Доступна по адресу: `http://localhost:8000/docs`
---
## 🗂️ Рабочие области (Workspaces)
Рабочие области — это именованные группы серверов Proxmox с ограниченным доступом пользователей. Они позволяют администраторам разделять инфраструктуру и давать пользователям доступ только к их серверам.
### Ключевые понятия
| Понятие | Описание |
|---|---|
| **Workspace** | Именованная группа с опциональным цветом и описанием |
| **Default Workspace** | Создаётся автоматически; все серверы по умолчанию в ней |
| **Назначение сервера** | Один сервер может принадлежать нескольким рабочим областям |
| **Назначение пользователя** | Пользователи видят только назначенные им области |
### Управление рабочими областями (админ)
1. Откройте **Настройки → Workspaces** (пункт бокового меню) или перейдите на `/workspaces`
2. Нажмите **New Workspace** для создания
3. Назначьте серверы на вкладке **Servers** внутри детали области
4. Назначьте пользователей на вкладке **Users**
### Переключение рабочих областей (боковая панель)
- Активная область показана в боковой панели под логотипом
- Кликните имя области, чтобы открыть переключатель
- Выберите область — все списки ВМ/серверов сразу фильтруются
- Выберите **All Workspaces** (только админ), чтобы снять фильтр
### API
| Метод | Эндпоинт | Описание |
|---|---|---|
| `GET` | `/api/workspaces` | Список областей (фильтруется для не-админов) |
| `POST` | `/api/workspaces` | Создать область (админ) |
| `GET` | `/api/workspaces/{id}` | Деталь области с серверами/пользователями |
| `PUT` | `/api/workspaces/{id}` | Обновить область (админ) |
| `DELETE` | `/api/workspaces/{id}` | Удалить область (админ) |
| `POST` | `/api/workspaces/{id}/servers` | Добавить сервер в область |
| `DELETE` | `/api/workspaces/{id}/servers/{server_id}` | Убрать сервер из области |
| `POST` | `/api/workspaces/{id}/users` | Добавить пользователя в область |
| `DELETE` | `/api/workspaces/{id}/users/{user_id}` | Убрать пользователя из области |
Передавайте заголовок `X-Active-Workspace: {id}` в запросах API для фильтрации ответов по области.
---
## 👤 Назначение серверов пользователю
Назначение серверов позволяет админам напрямую связывать конкретные серверы Proxmox с отдельными пользователями. В сочетании с рабочими областями это даёт тонкий контроль: пользователь видит только серверы, которые **одновременно** и в его рабочей области, **и** явно назначены ему.
### Как это работает
1. Пользователь принадлежит одной или нескольким **рабочим областям**
2. Админ назначает пользователю конкретные **серверы** (из серверов, разделяющих хотя бы одну область с пользователем)
3. Пользователь видит только пересечение: серверы-области ∩ назначенные-серверы
### Управление назначениями (UI)
1. Откройте страницу **Пользователи**
2. Нажмите **Edit** для пользователя
3. На вкладке **Servers** — отметьте/снимите серверы; несовместимые серверы (другая область) подсвечены
4. Save — назначения применяются немедленно
### REST API
| Метод | Эндпоинт | Описание |
|---|---|---|
| `GET` | `/api/users/{id}/servers` | Назначенные серверы пользователя |
| `GET` | `/api/users/{id}/server-assignments` | Все серверы с флагами `assigned` и `compatible` |
| `PUT` | `/api/users/{id}/servers` | Задать серверы (заменяет текущий список); тело: `{"server_ids": [1,2]}` |
Возвращает `409 workspace_conflict`, если какой-либо сервер не разделяет ни одной области с пользователем.
---
## 🖥️ Владелец ВМ / LXC
У любой ВМ или LXC-контейнера может быть **владелец** — обычный пользователь, ответственный за этот инстанс. Владелец отображается в списке ВМ.
### Изоляция
Владение применяется везде, где непривилегированный пользователь видит инстансы, а не только в списке инстансов. Эндпоинты живых ресурсов — `/api/resources/all` (дашборд) и `/api/{server_id}/resources` (страница узла/сервера) — фильтруют ВМ/LXC по владельцу (сопоставляя каждый `vmid` с кэшем `vm_instances`), поэтому пользователь только с `proxmox.view` не может видеть инстансы других владельцев через страницу узла. **Админы** и роли с **`vm:manage`** освобождены от этого и видят всё.
**Владелец теперь проставляется автоматически при создании** (`vm_create`/`ct_create`), в том числе когда ВМ создаёт админ от имени другого пользователя — деплой инжектирует SSH-ключ именно этого владельца (объединённый с любыми вставленными вручную или взятыми из библиотеки ключами), а не ключ создавшего.
Инстансы, синхронизированные из Proxmox до появления владения, или созданные до исправления выше, имеют `owner_id = NULL`. Разовая миграция бэкфиллит их автоматически из `deploy_tasks` и `audit_logs` (записи `vm_create`/`ct_create`/`lxc_create`), но только когда все доступные данные указывают на одного и того же существующего пользователя — неоднозначные случаи (например, переиспользованный VMID) намеренно остаются без владельца. Чтобы назначить владельца тому, что осталось, запустите вспомогательный скрипт из образа бэкенда:
```bash
docker compose exec app python assign_instances_owner.py
```
### Назначение владельца (админ)
Откройте страницу деталей ВМ → кнопка **Owner** → выберите пользователя → Save.
### REST API
| Метод | Эндпоинт | Описание |
|---|---|---|
| `GET` | `/api/{server_id}/vm/{vmid}/owner` | Текущий владелец + список пользователей |
| `PUT` | `/api/{server_id}/vm/{vmid}/owner` | Задать или очистить владельца (`{"user_id": null}` для очистки) |
---
## 🎯 Квоты
Квоты ресурсов на пользователя ограничивают, сколько пользователь может выделить. Ограничиваются четыре метрики, каждая суммируется по **неудалённым, не-шаблонным** инстансам пользователя:
| Метрика | Единица | Поле квоты |
|---|---|---|
| Инстансы | количество | `max_instances` |
| vCPU | ядра | `max_cores` |
| RAM | МБ | `max_memory_mb` |
| Диск | ГБ | `max_disk_gb` |
- **Без ограничений по умолчанию** — пользователь без строки квоты или с пустой метрикой (`NULL`) не ограничен по этой метрике. Админы и ненастроенные пользователи никогда не блокируются.
- **Применение** — развёртывание VM/LXC, которое превысит любую метрику, отклоняется с **HTTP 429** и двуязычным сообщением (`Превышена квота … / Quota exceeded …`) с прогнозируемым значением и лимитом.
- **Источник использования** — текущее использование вычисляется из кэша `vm_instances` (`owner_id`), где RAM хранится в МБ, а диск — в ГБ (без конвертации).
### Два уровня блокировки на клиенте
Реально применяет ограничение только backend'ный HTTP 429, но интерфейс теперь предупреждает заранее, используя в обоих местах одну и ту же функцию `exhaustedMetrics()` (`features/instances/quota.ts`), чтобы они не могли разойтись:
- **Свободного места совсем не осталось** (использовано ≥ лимита хотя бы по одной метрике) — сама точка входа **«Создать инстанс»** отключается, а подсказка называет, какие именно лимиты исчерпаны. Ни одна конфигурация не влезет, поэтому мастер даже не открывается.
- **Место есть, но выбранная конфигурация его превысит** — мастер открывается как обычно; начиная с шага конфигурации называется метрика, которая была бы превышена, а **«Далее»**/**«Развернуть»** заблокированы, пока запрос не уменьшат.
Ни одна из блокировок не применяется к **админам** — их личная квота не имеет отношения к инстансу, который они создают для кого-то другого, а показанные им цифры носят чисто информационный характер.
### Управление квотами (админ)
Страница **Пользователи** → диалог квот. Живые полосы использования показывают текущее потребление против каждого лимита. Оставьте поле пустым для *без ограничений*. Требует право `quota:manage` (`quota:view` для чтения).
### Self-service
Каждый пользователь видит свои лимиты и текущее использование на странице **Профиль**. Мастер **создания инстанса** показывает остаток квоты подсказкой на шаге конфигурации.
### REST API
| Метод | Эндпоинт | Описание |
|---|---|---|
| `GET` | `/api/users/{id}/quota` | Лимиты пользователя + текущее использование (требует `quota:view`) |
| `PUT` | `/api/users/{id}/quota` | Задать лимиты; `null` для метрики = без ограничений (требует `quota:manage`) |
| `GET` | `/api/quota` | Собственные лимиты + использование текущего пользователя (self-service) |
---
## 🔧 Устранение неполадок
### Проблемы запуска
#### Контейнер не запускается
```bash
# Проверить логи
docker compose logs app
# Проверить статус
docker compose ps
```
#### Ошибка подключения к базе данных
```bash
# Проверить, запущена ли БД
docker compose logs db
# Перезапустить
docker compose restart db
docker compose restart app
```
### Проблемы Proxmox
#### «Connection refused»
- Проверьте IP-адрес сервера
- Проверьте, что порт 8006 открыт
- Проверьте файрвол на Proxmox
#### «Authentication failed»
- Проверьте логин/пароль
- Используйте формат `user@pam` или `user@pve`
- Попробуйте создать API Token
### Проблемы уведомлений
#### Email не отправляется
1. Проверьте настройки SMTP в `backend/.env`
2. Для порта 465 требуется SSL
3. Для порта 587 требуется STARTTLS
4. Проверьте «пароль приложения» для Yandex/Gmail
```bash
# Проверить переменные в контейнере
docker compose exec app env | grep SMTP
```
#### Telegram не работает
1. Проверьте токен бота
2. Отправьте боту `/start`
3. Проверьте Chat ID
4. Убедитесь, что бот не заблокирован
#### Кнопка «Тест» зависает
- Проверьте логи: `docker compose logs app --tail 50`
- SMTP-сервер может быть недоступен
- Таймаут — 10 секунд
### Проблемы VNC / терминала
#### «VNC connection failed» (QEMU)
- Проверьте, что ВМ запущена
- Убедитесь, что VNC включён в настройках узла Proxmox
- Попробуйте перезапустить ВМ
- Проверьте, что у API-пользователя Proxmox есть привилегия `VM.Console`
#### «Connection closed» сразу при открытии VNC
- Это была известная ошибка, исправленная в v1.3.0 (noVNC получал уже открытый WebSocket вместо URL)
- Обновитесь до v1.3.0 или новее
#### Терминал не подключается (LXC)
- Проверьте, что контейнер **запущен** (не остановлен и не приостановлен)
- Проверьте, что у API-пользователя Proxmox есть привилегия `VM.Console` на узле
- Проверьте логи: `docker compose logs app --tail 50`
### Проблемы команд
#### «Guest Agent not running»
```bash
# В ВМ выполнить:
systemctl status qemu-guest-agent
systemctl start qemu-guest-agent
```
#### Команда зависает
- Таймаут — 30 секунд
- Не используйте интерактивные команды
- Проверьте, что ВМ отвечает
### Проблемы производительности
#### Высокое использование памяти
```bash
# Перезапустить контейнеры
docker compose restart
# Очистить логи
docker system prune -f
```
#### Медленная загрузка страниц
- Увеличьте интервал обновления в настройках
- Проверьте сеть до сервера Proxmox
### Проблемы с учётной записью
#### Админ забыл пароль
Если админ забыл пароль, его можно сбросить через командную строку:
**Вариант 1: Сброс к паролю по умолчанию**
```bash
# Выполнить внутри контейнера app
docker compose exec app python -c "
from app.db import get_db
from app.models import User
from app.auth import get_password_hash
db = next(get_db())
admin = db.query(User).filter(User.username == 'admin').first()
if admin:
admin.hashed_password = get_password_hash('admin123')
db.commit()
print('Admin password reset to: admin123')
else:
print('Admin user not found')
db.close()
"
```
**Вариант 2: Задать свой пароль**
```bash
# Замените YOUR_NEW_PASSWORD на нужный пароль
docker compose exec app python -c "
from app.db import get_db
from app.models import User
from app.auth import get_password_hash
db = next(get_db())
admin = db.query(User).filter(User.username == 'admin').first()
if admin:
admin.hashed_password = get_password_hash('YOUR_NEW_PASSWORD')
db.commit()
print('Admin password has been reset')
else:
print('Admin user not found')
db.close()
"
```
**Вариант 3: Сброс пароля любого пользователя**
```bash
# Замените USERNAME на нужное имя пользователя
docker compose exec app python -c "
from app.db import get_db
from app.models import User
from app.auth import get_password_hash
db = next(get_db())
user = db.query(User).filter(User.username == 'USERNAME').first()
if user:
user.hashed_password = get_password_hash('newpassword123')
db.commit()
print('Password reset for user: USERNAME')
else:
print('User not found')
db.close()
"
```
> ⚠️ **Заметка о безопасности:** после сброса пароля немедленно войдите и смените его на надёжный через веб-интерфейс.
---
## ❓ FAQ
### Общие вопросы
**В: Можно ли использовать без Proxmox?**
О: Нет, панель специально разработана для управления Proxmox.
**В: Сколько серверов можно добавить?**
О: Не ограничено.
**В: Поддерживается ли кластер Proxmox?**
О: Да, добавьте любой узел кластера.
### Безопасность
**В: Пароли хранятся в открытом виде?**
О: Нет, используется bcrypt-хэширование.
**В: Как сменить SECRET_KEY?**
О: Измените в `.env` и перезапустите. Все сессии будут инвалидированы.
### Уведомления
**В: Почему не приходят письма?**
О: Проверьте настройки SMTP, особенно порт и TLS.
**В: Можно ли отключить уведомления?**
О: Да, в настройках уведомлений.
**В: Как задать тихие часы?**
О: В настройках уведомлений укажите период (напр., 23:00 – 07:00).
### Интеграции
**В: Есть ли API для интеграции?**
О: Да, REST API с документацией по адресу `/docs`.
**В: Можно ли добавить webhooks?**
О: Пока нет, но запланировано в будущих версиях.
---
## 📞 Поддержка
- **GitHub Issues**: [Создать issue](https://github.com/your-repo/server-panel/issues)
- **Документация**: этот файл (WIKI.ru.md)
- **Changelog**: [CHANGELOG.ru.md](CHANGELOG.ru.md)
---
*Последнее обновление: 24 июля 2026*
*Версия: 1.13.0*