Files
pvemanager/CHANGELOG.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

562 lines
88 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 документируются в этом файле.
---
## [Unreleased]
### 🐛 Исправления
- **В образах Rocky Linux, AlmaLinux, CentOS Stream и Fedora разблокирован `guest-exec`** — пакет `qemu-guest-agent` в RHEL-семействе запускает агента с allow-list, в котором нет `guest-exec`, `guest-exec-status` и `guest-file-*` (`FILTER_RPC_ARGS` в `/etc/sysconfig/qemu-ga`; в пакетах постарше то же самое задавалось через `BLOCK_RPCS`/`BLACKLIST_RPC`). Из-за этого в таких ВМ не работали выполнение скриптов, выдача дополнительных IP и расширение ФС после ресайза диска
- **Пять образов зеркала пересобраны** — релиз `2026-08-22`, встроенный каталог переключён на него; Ubuntu и Debian не затронуты и остаются в прежних релизах, поэтому старые версии панели продолжают работать
- **`patch-cloud-images.sh` снимает ограничение при сборке** (отключается через `UNBLOCK_RPC=0`) и умеет собирать часть каталога — `ONLY="rocky alma centos fedora"`. Панель же, наткнувшись на запрет, объясняет в тексте ошибки, что и где поправить в госте
---
## [v1.18.0] - 2026-08-22
### 🌐 Несколько IP-адресов на инстанс
- **Инстанс больше не ограничен одним адресом** — у гостя может быть основной адрес и любое количество дополнительных. Раньше вся логика исходила из «один гость — одна аллокация»: `find_allocation_by_resource()` возвращал первую попавшуюся запись, `release_ip_by_vmid()` освобождал один адрес (остальные после удаления гостя оставались занятыми навсегда), а инструмент связывания пропускал гостей, у которых аллокация уже была
- **На вкладке «Сеть» инстанса появилась карточка «IP-адреса»** — показывает все адреса гостя с их состоянием и позволяет выдать новый (следующий свободный в сети/пуле или введённый вручную), повторить применение, сделать основным или освободить. В колонке IP списка инстансов показывается основной адрес и бейдж **+N** для остальных, а в таблице аллокаций основной помечен
- **Дополнительные адреса навешиваются внутри гостя как alias** — через существующий движок скриптов: для LXC это `pct exec`, для ВМ — QEMU guest agent. Адрес поднимается через `ip addr add`, сеть гостя при этом не перезапускается, поэтому работающий гость не теряет связь
- **Адреса переживают перезагрузку гостя** — Proxmox пересоздаёт сетевую конфигурацию при каждом старте (pve-container переписывает `/etc/network/interfaces`, `ifcfg` или keyfile NetworkManager из `netN`), стирая любой alias, добавленный в родной стек. Небольшой oneshot systemd-юнит заново поднимает записанные адреса после `network-online.target`; каталог `/etc/systemd` Proxmox не трогает. Родные стеки (ifupdown, netplan, NetworkManager, sysconfig/network-scripts для RHEL-подобных гостей) по-прежнему настраиваются, поэтому состояние может быть составным, например `nm+systemd`
- **Адрес резервируется, даже если применить его не удалось** — остановленный гость, отсутствующий guest agent или недоступный SSH к ноде оставляют аллокацию в состоянии `pending` с указанием причины, а кнопка **«Применить»** повторяет попытку позже. Нераспознанный сетевой стек даёт `runtime_only` — предупреждение, что адрес не переживёт перезагрузку
- **Новые эндпоинты** — `GET/POST /ipam/api/guests/{server_id}/{vmid}/addresses`, `POST …/{allocation_id}/apply`, `POST …/{allocation_id}/primary`, `DELETE …/{allocation_id}`; список аллокаций принимает фильтры `proxmox_server_id` / `proxmox_vmid`, а список инстансов отдаёт `ips[]` рядом с основным `ip`
- **Миграция 43** добавляет в `ipam_allocations` колонки `is_primary`, `assignment_kind`, `target_interface` и `apply_*`, помечая существующие записи основными. Первый адрес гостя теперь помечается автоматически, из какого бы из шести мест выдачи он ни пришёл
- **Освобождение адреса сначала снимает его с гостя**, затем освобождает в IPAM; основной адрес задаётся конфигом интерфейса и из этой карточки не освобождается
### 🔍 Исправления определения адресов в IPAM
- **LXC-контейнеры наконец показывают свой IP** — `get_container_interfaces()` искал ключи `ipconfigN`, которые бывают только у QEMU-гостей с cloud-init. LXC хранит адрес внутри строки `netN`, поэтому метод не возвращал ничего и колонка IP у всех контейнеров оставалась пустой. Теперь используется парсер `netN`, а для гостей на DHCP — запрос `/nodes/{node}/lxc/{vmid}/interfaces`
- **«Сканировать и связать» регистрирует все адреса гостя**, а не только первый, и больше не пропускает уже связанных гостей — сменившийся или добавленный адрес раньше оставался незамеченным навсегда
- **MAC-адрес переносится в аллокацию** при связывании — берётся с того интерфейса, на котором найден адрес
- **Дублирующийся адрес больше нельзя отобрать у живого гостя** — когда два гостя заявляют один IP (обычно из-за устаревшего `ip=` в конфиге остановленного контейнера), владелец сохраняется, а конфликт возвращается с объяснением вместо тихой перезаписи записи
- **Аллокации больше не сопоставляются по имени ресурса** — из-за этого два одноимённых гостя на разных серверах показывали чужой адрес
- **История IPAM больше не падает на значениях даты/времени** — раньше это обрывало повторное связывание ошибкой 500
- **IP гостя перепроверяется не чаще раза в 5 минут**, а не при каждом синке кэша (каждые 10 секунд) — это убрало сотни бессмысленных обращений к Proxmox в минуту
### 🐛 Прочие исправления
- **Скрипты с не-ASCII текстом снова выполняются на ВМ** — Proxmox кодирует payload для guest agent в base64 силами Perl, а тот падает с `Wide character in subroutine entry`, как только в скрипте встречается не-ASCII (достаточно комментария на кириллице). Теперь payload передаётся байтами
- **Поиск следующего свободного IP умеет сканировать всю подсеть**, когда в сети нет пула с auto-assign, пропуская адреса нод и известные адреса гостей — чтобы не выдать адрес, который уже занят в сети
---
### 🖥️ Консоль LXC — ошибки termproxy и чёрный экран
- **⚠️ Ломающее изменение: эндпоинт `GET /api/{server_id}/container/{vmid}/terminal` удалён.** WebSocket-эндпоинт `/proxmox/ws/terminal/{server_id}/{node}/{vmid}` создаёт termproxy-сессию сам, поэтому REST-вызов не использовался панелью и при каждом вызове оставлял на ноде висячую сессию. Внешним клиентам следует открывать WebSocket напрямую
- **Открытие консоли LXC больше не оставляет на ноде задачу с ошибкой** — `failed waiting for client: timed out` / `TASK ERROR: command '/usr/bin/termproxy 5900 ... vzctlconsole<id>' failed`. Страница консоли создавала termproxy-сессию через REST (используя из ответа только поле `node` и выбрасывая порт и тикет), затем вторую через WebSocket-эндпоинт и подключалась ко второй. К первой не подключался никто — Proxmox ждал клиента ~10 секунд и завершал задачу ошибкой
- **Консоль больше не показывает чёрный экран при статусе «connected» в dev-режиме** — React StrictMode монтирует эффект дважды, а connect-функции асинхронны, поэтому cleanup эффекта срабатывал до того, как они записывали `wsRef`/`termRef`, и ничего не чистил. В одном контейнере оказывалось два xterm: видимый был осиротевшим, а живой уходил за границу области с `overflow-hidden`. Теперь у сессии есть флаг отмены, а монтирование и кнопка «Переподключиться» идут через единую точку входа, которая сначала гасит предыдущую сессию. Тот же флаг применён к странице shell ноды
---
### 🔍 Диалог NFS-хранилища — поиск доступных экспортов
- **Добавлена кнопка поиска экспортов** рядом с полем «Сервер» при добавлении NFS-хранилища — вызывает `GET /nodes/{node}/scan/nfs` Proxmox и превращает поле пути экспорта из текстового ввода в выпадающий список с реальными экспортами, найденными на целевом сервере, вместо ручного набора пути наугад
- Новый эндпоинт `GET /api/backups/scan-nfs/{server_id}?server=<ip>` на бэкенде — сканирование выполняется через первую доступную ноду сервера
---
## [v1.17.0] - 2026-08-20
### 💾 Перенос дисков LXC-контейнера на другое хранилище
- **Перенос диска теперь работает для LXC**, а не только для QEMU-ВМ — раньше действие было жёстко привязано к VM-эндпоинту `move_disk` и было недостижимо для контейнеров (нет вкладки «Оборудование», а детектор строк диска распознавал только имена устройств ВМ). `rootfs`/`mp\d+` теперь распознаются как дисковые устройства LXC, на вкладку «Настройки» для LXC-инстансов добавлена карточка **«Перенести диск»**
- Новый эндпоинт `POST /api/{server_id}/container/{vmid}/disk/move` вызывает `lxc/{vmid}/move_volume` Proxmox; список целевых хранилищ теперь фильтруется по поддерживаемому типу содержимого (`rootdir` для LXC, `images` для ВМ) вместо показа всех хранилищ подряд
- **Перенос заблокирован на запущенных контейнерах — с пошаговым сценарием вместо ошибки** — Proxmox отклоняет `move_volume` на работающем LXC (`move_disk` у QEMU работает «на живую», `move_volume` у LXC — нет). Теперь такая попытка открывает диалог подтверждения, который останавливает контейнер, переносит диск и запускает его обратно, показывая прогресс на каждом шаге; контейнер перезапускается в любом случае, даже если сам перенос завершился ошибкой. Для ВМ и уже остановленных контейнеров показывается обычный диалог подтверждения
### 📊 Список инстансов — сворачиваемая панель метрик
- **CPU/RAM/Диск/Сеть/Диск I/O/Аптайм убраны из всегда видимых колонок таблицы** в раскрывающуюся панель под строкой, которая открывается кликом по имени инстанса — по образцу дисклоужер-паттерна из VMmanager. В панели появилась ссылка **«Параметры»**, ведущая прямо на вкладку настроек инстанса, и более понятные карточки для скорости сети/диска
- Полосы vCPU/RAM расширены и увеличены (ширина полосы и размер шрифта подписи/деталей) для более быстрого визуального восприятия в свёрнутой панели
### ✨ Страница «Топология сети»
- **Новая страница `/topology`** — интерактивная карта инфраструктуры: панель → кластер → нода Proxmox → VM/LXC на базе React Flow. На карточках нод — живые индикаторы CPU/RAM, у гостей видны статус, IP, bridge/VLAN и теги
- **Фильтры** — сужение графа до одного подключения, фильтр гостей по статусу (все / запущенные / остановленные) и схлопывание гостей ноды в один узел при превышении настраиваемого порога (запоминается в браузере); свернувшуюся ноду можно раскрыть по отдельности
- **Инструменты холста** — зум, панорама, вписать в экран, полноэкранный режим, миникарта, легенда и экспорт всего графа в PNG/SVG
- **Новый API `GET /proxmox/api/network/topology`** — один агрегированный запрос вместо обхода серверов/нод/SDN/VM; требует право `network:view`, учитывает рабочие области и изоляцию VM по владельцу. Недоступное подключение отдаёт данные из кэша с предупреждением, а не ломает страницу
- **Парсер `netN`** — строки сетевых карт гостей теперь разбираются (bridge, VLAN-тег, MAC, модель, firewall, rate, MTU) для обоих диалектов, QEMU и LXC; доступно через `include_nics=true`
---
## [v1.16.1] - 2026-08-17
### 🎨 Интерфейс
- **Шрифт Inter Variable** — фронтенд переведён на вариативный шрифт Inter с увеличенной шкалой размеров для более чёткой и единообразной типографики по всей панели
### 🔧 Исправления деплоя
- **CLI `pve` устанавливается без root** — `deploy.sh` теперь пробует `sudo` для установки CLI в `/usr/local/bin`, а при недоступности root откатывается на `~/.local/bin` (с автоматической настройкой PATH); ранее авто-установка молча пропускалась, и команда `pve` оставалась недоступной после деплоя
- **Права файла CLI `pve`** — установленный скрипт теперь явно получает права `755` вместо зависимости от umask; ранее `sudo cp` мог оставить файл без права на чтение для не-root пользователей, вызывая «Permission denied» при каждом запуске
- **SSL больше не откатывается при деплое** — `verify_nginx_running` теперь использует `docker compose ps --format '{{.State}}'` (возвращает `"running"` мгновенно) вместо grep по человекочитаемому столбцу STATUS (который показывает `"Up Xs (health: starting)"` первые 30 секунд до первого healthcheck); со старым таймаутом 20 секунд функция всегда завершалась по таймауту и откатывала конфиг на HTTP, хотя SSL-сертификат был успешно получен
---
## [v1.16.0] - 2026-08-14
### 🌍 i18n — Полный проход по переводам
Масштабная зачистка оставшихся захардкоженных и непереведённых строк интерфейса:
- **Дашборд и инстансы** — диалоги действий, карточки ресурсов, метки статусов и описания массовых операций полностью переведены (EN + RU)
- **Страница «Образы»** — браузер образов, диалоги загрузки/импорта, менеджер зеркал — полный перевод
- **App Store** — добавлены недостающие ключи для мастера установки, состояний операций и меток страницы приложения
- **Ноды** — метки силовых действий (`Запуск`, `Перезагрузка`, `Выключение`) и общие строки администрирования нод переведены
- **Скрипты и сертификаты** — разделы каталога скриптов и TLS-сертификатов добавлены в оба локейла
- **Роли** — метки всех RBAC-разрешений, включая `remote_migrate`, переведены на русский
- **Графики, мастер, хранилища** — ранее захардкоженные русские строки вынесены в файлы локали и переведены на оба языка
- **Массовая миграция и Access realm** — добавлены ключи для действий массовой миграции и меток реалмов Proxmox
- **Критические исправления** — несколько строк, молча откатывавшихся на английский в русском интерфейсе, теперь корректно отображаются на русском
### 🔑 SSH-ключи
- **Смена SSH-ключей при смене владельца** — когда администратор переназначает VM/LXC другому пользователю, инстанс автоматически получает SSH-ключ нового владельца (ключ предыдущего при этом удаляется), так что учётные данные для доступа всегда соответствуют текущему владельцу
### 📦 Облачные образы
- **`patch-cloud-images.sh`** — новый вспомогательный скрипт, который патчит официальные облачные образы, добавляя `qemu-guest-agent`, и публикует их в GitHub-зеркало; позволяет получить живые метрики и функции гостевого агента без установки агента после деплоя
- **Каталог образов amd64** — записи образов amd64 по умолчанию теперь указывают на GitHub-зеркало (с предустановленным qemu-guest-agent) и включают контрольные суммы `sha256` для проверки целостности
### 🐛 Исправления
- **Обзор дисков VM** — loop-устройства (`/dev/loop*`) и системные разделы (`/dev/sr*`, `/boot`, `/snap`) включались в карточки использования диска; теперь они отфильтровываются и отображаются только диски с данными
- **Повторная загрузка облачных образов** — закешированные импортированные образы раздавались с диска даже при наличии контрольной суммы и изменении файла на удалённом источнике; теперь кеш инвалидируется при несовпадении суммы
- **Иконка видимости пароля** — значок глаза во всех полях пароля (вход, смена пароля, парольная фраза SSH-ключа, диалоги API-ключей) был инвертирован: открытый глаз отображался, когда значение было скрыто, и наоборот; теперь состояние иконки корректно отражает видимость значения
---
## [v1.15.0] - 2026-08-13
### 🚀 Установка одной командой
- **`bootstrap.sh`** — устанавливает панель одной командой как на обычном хосте Debian/Ubuntu, так и напрямую на хосте Proxmox VE. На хосте PVE скрипт создаёт LXC на Debian 12, устанавливает панель внутри через существующий `deploy.sh`, создаёт локальный API-токен `root@pam` и регистрирует этот хост как первую ноду панели — пароль от Proxmox никуда не передаётся
- Скрипт переживает сбой `apt-get update` из-за недоступного enterprise-репозитория на неподписанном хосте PVE вместо того, чтобы прерывать весь bootstrap ещё до установки `git`/`curl`
- Регистрация хоста теперь идёт по правильному пути `/proxmox/api/servers` (раньше использовался `/api/servers`, отвечавший 404, — роутер Proxmox подключён под префиксом `/proxmox`)
- Убран небезопасный дефолт `pvemanager_secure_password` для `POSTGRES_PASSWORD` в `compose.yml` — на любом пути установки `.env` с реальным паролем уже создаётся до запуска стека
### 💿 Образы и инстансы
- **Переработка извлечения/монтирования ISO** — страница «Образы» теперь показывает реальные ISO-файлы, уже лежащие в хранилище ноды (включая те, что появились там до подключения ноды к панели); «Отсоединить» переименовано в **«Извлечь»**, текущий смонтированный образ отображается прямо в списке выбора, а новый чекбокс **«загрузка с диска»** / **«загрузка с этого ISO»** управляет порядком загрузки при извлечении/монтировании
- Применение нового порядка загрузки перезагружает гостевую систему через **гибридный рестарт** — штатное ACPI-выключение с откатом на принудительную остановку, затем запуск — что убирает таймаут ping гостя на свежеустановленной ОС, где ещё нет QEMU Guest Agent
### 🔒 RBAC и доступ
- **`/profile` отделён от `/settings`** — настройки аккаунта (профиль, пароль, 2FA, SSH-ключи) переехали на собственную страницу `/profile`, не требующую никакого разрешения, и открываются из меню пользователя и Ctrl+K. Теперь отзыв `setting:view`, чтобы не пускать пользователя в настройки панели, больше не блокирует доступ к его собственному аккаунту. `/settings` по-прежнему скрывает общепанельные вкладки за `setting:view`, сохранение — за `setting:update`, безопасность — за `setting:manage`, а SMTP/Telegram — только для админов
- **Маршруты теперь защищены по правам**, а не просто скрыты из сайдбара — раньше `/users`, `/logs` и `/settings` (и их вкладки) открывались по прямому переходу на URL кому угодно, а бэкенд лишь отвечал 403 на конкретные запросы; теперь пользователь без доступа перенаправляется на первую доступную ему страницу
- Миграция 42 снимает `setting:view` с ролей, у которых нет также `setting:update`/`setting:manage`, — этот доступ раньше выдавался только ради общей страницы настроек, включавшей профиль
### 👤 Владение инстансами и квоты
- **Исправлено назначение владельца при создании** — новые VM/LXC оставались с `owner_id = NULL`, потому что задача синхронизации, регистрирующая ещё клонирующуюся VM, опережала флоу деплоя и первой создавала запись в БД; теперь владелец проставляется и на этом пути обновления записи. VM, созданная админом от имени пользователя, теперь получает SSH-ключ именно **этого пользователя**, а не админа
- **Миграция для бэкфилла** владельца у инстансов, созданных до исправления выше: владелец восстанавливается из `deploy_tasks` и `audit_logs` и проставляется только тогда, когда все доступные данные указывают на одного и того же существующего пользователя; неоднозначные случаи остаются без владельца — для них нужен ручной запуск `assign_instances_owner.py`
- **Кнопка «Создать инстанс» отключается**, когда по какой-то квоте совсем не осталось свободного места, а не только отказывает внутри мастера — подсказка называет, какие именно лимиты исчерпаны (проверка пропускается для админов, чья личная квота не имеет отношения к инстансам, создаваемым для других)
- Блокировка перед открытием мастера теперь отделена от блокировки внутри него: полностью исчерпанная квота не даёт открыть мастер вовсе; квота, которую превысит именно выбранная конфигурация, открывает мастер, но блокирует «Далее»/«Развернуть» с указанием превышенного показателя
### 🐛 Исправления
- **Продакшн-деплой не отключал открытые порты** — `ports: []` в `compose.prod.yml` на деле ничего не убирал (Compose при слиянии нескольких `-f` файлов не заменяет списковые поля, а объединяет их), поэтому SSL-деплой продолжал отвечать без шифрования на портах `8000`/`3001` параллельно с HTTPS. Исправлено через тег `!reset`
- **SSL-деплой сообщал о неудаче даже после успешного выпуска сертификата** — проверка успеха не могла прочитать директорию certbot, принадлежащую root и имеющую права 0700, если деплой запускался не от root; теперь сертификат проверяется изнутри контейнера, а откат на HTTP выполняется только если nginx реально не поднялся с новым конфигом
- **Первая загрузка фронтенда могла показать пустую страницу** — общий лимит частоты запросов (`10r/s`) применялся и к статике, отклоняя часть из ~86 подгружаемых чанков SPA с 503; после исправления этого проявился второй лимитер — `limit_conn addr 10` ограничивал всю страницу 10 одновременными HTTP/2-потоками (браузер мультиплексирует всю загрузку страницы через одно соединение), давая тот же эффект пустой страницы. Теперь у статики отдельная зона лимита частоты, а оба лимита `limit_conn` подняты
- **Воркеры nginx не могли открыть достаточно файловых дескрипторов** под `worker_connections 2048` — дефолтный `RLIMIT_NOFILE` Docker в 1024 незаметно урезал реальный потолок вдвое; поднят до 8192
- Убраны `listen 443 ssl http2` (устарела с nginx 1.25.1) и `ssl_stapling` (у текущих сертификатов Let's Encrypt нет OCSP-респондера) — обе директивы выдавали предупреждения при периодическом reload nginx
- **Аптайм ноды показывался неверно в мультинодовых кластерах** — карточка сервера брала `Math.max` по аптайму всех нод кластера вместо аптайма именно текущей, поэтому перезагрузка одной ноды могла по-прежнему показывать более высокий аптайм другой
- **Тост о завершении сессий никогда не переводился** — сообщение собиралось на бэкенде на Python и возвращалось как есть, поэтому в русском интерфейсе показывалось "Terminated 0 sessions"; теперь количество возвращается отдельным полем, а фраза собирается на клиенте (для русского нужно четыре формы множественного числа против двух в английском)
---
## [v1.14.0] - 2026-07-24
### 🐛 Исправления
- **Смена рабочей области не сбрасывала фильтры инстансов** — фильтры по серверу, узлу и столбцам на странице «Инстансы» сохраняли значения, выбранные ещё в предыдущей рабочей области, из-за чего список VM новой области мог отфильтроваться до пустоты. Теперь при переключении рабочей области все эти фильтры сбрасываются
- **Фильтр по колонке «Узел» не обновлялся после первого рендера** — список доступных узлов в нём был замемоизирован на объекте `Column` из TanStack, а этот объект сохраняет свою идентичность при обновлении данных таблицы, поэтому список замирал на тех узлах, что были видны при первом открытии — включая узлы серверов из уже покинутой рабочей области. Теперь значение читается напрямую из faceted-данных таблицы при каждом рендере
---
## [v1.13.0] - 2026-07-24
### 🛒 App Store
- **Выбор хранилищ в golden-шаблоне** — в диалоге «Золотой LXC-шаблон» текстовые поля для хранилища шаблона (`vztmpl`) и rootfs (`rootdir`) заменены на выпадающие списки, заполняемые реальными хранилищами выбранного сервера/ноды. По умолчанию подставляются `local`/`local-lvm`, если они есть, иначе — первое доступное хранилище
### 📜 Scripts
- **Поиск по названию в каталоге скриптов** — фильтрация ручных, Git и Community-Scripts записей прямо на странице Scripts
---
## [v1.12.0] - 2026-07-24
### 🔗 Кластер
- **Импорт существующего кластера** — ноды, уже объединённые в кластер Proxmox в обход панели (через `pvecm` или веб-интерфейс Proxmox), теперь можно привязать прямо на странице «Кластер», а не только через собственный флоу панели «Создать»/«Присоединить». Обнаружение читает `cluster/config/join` на выбранной ноде, сопоставляет её nodelist с уже известными серверами панели по IP/hostname и привязывает совпавшие записи к этой группе кластера — никакая `pvecm`-операция не выполняется, меняется только учёт внутри панели
---
## [v1.11.0] - 2026-07-24
### 🔌 Серверы и подключение
- **Автосоздание API-токенов** — сервер можно подключить по логину и паролю: панель один раз аутентифицируется, создаёт на ноде собственный API-токен (`privsep=0`) и дальше работает по нему. Токен отзывается из UI Proxmox, не даёт SSH- и веб-логина и виден в аудите PVE под своим именем. Уже подключённые по паролю серверы переводятся на токен без переподключения кнопкой **«Перевести на API-токен»**
- **Отклонённые учётные данные vs. недоступная нода** — раньше офлайн-сервер показывал единое «Не удалось подключиться»; теперь протухший/отклонённый токен (401, обычно остаётся после того, как вступление ноды в кластер заменяет `user.cfg`/`token.cfg`) получает собственное состояние `auth_error` с подсказкой о причине
- **Доступность storage по нодам** — storage может быть объявлен на весь кластер, но отсутствовать на конкретной ноде (нет volume group или пула); теперь это видно, а не скрыто
- **Единые бейджи статуса** — все списки серверов (включая топологию кластера и назначение пользователей) рендерят один `ServerStatusBadge` вместо повторяющегося тернарника online/offline
- **Списки в рамках рабочего пространства** — `/api/cluster/topology` и `/api/resources/all` теперь учитывают активное рабочее пространство, а не отдают все серверы панели; живые метрики собираются параллельно, а не последовательно, поэтому один недоступный кластер больше не тормозит весь запрос
### 📊 Мониторинг
- **Вкладка «Графики» у ноды** — CPU, IO delay, память, load average, сеть, swap и корневая ФС на странице ноды, с пресетами `1h/24h/7d/30d/1y`
### 💿 Образы и создание инстансов
- **Загрузка локального файла** — ISO или `vztmpl` можно загрузить прямо со своего компьютера в storage ноды, в дополнение к загрузке по URL
- **Пустая VM для установки с ISO** — новый тип инстанса в мастере создания: VM с пустым диском и смонтированным первым по порядку загрузки ISO, гость сразу попадает в установщик через noVNC; выбор Windows задаёт OVMF, SATA-диск и сетевую карту e1000
- **Загрузка ISO** — ISO можно скачать на ноду по URL со страницы «Образы» или из сохранённого зеркала (зеркала теперь принимают `kind=iso`)
### 🔄 Реалтайм и UX
- Списки инстансов и рабочих пространств обновляются сразу после назначения серверов/пользователей, деплоя или удаления VM/контейнера — без ручного обновления
- Обзор ноды больше не мигает «нет данных», пока список инстансов ещё загружается
### 🛠 Надёжность
- **Самообновление панели восстанавливается после зависшей пересборки** вместо вечного «restarting — 80%»; по таймауту показывается понятное состояние «failed» со ссылкой на логи update-сервиса хоста
### 🎨 Тема
- Поверхности тёмной темы (фон, карточки, popover, сайдбар, границы) сдвинуты с нейтрального оттенка на сине-графитовый
### 📖 Документация
- Расширен раздел RBAC/Security (формат прав, порядок проверки, полный каталог прав, карта прав по страницам), добавлен полный русский перевод — **WIKI.ru.md**
---
## [v1.10.2] - 2026-07-23
### 🐛 Исправления
- **Шаблоны** — после авто-импорта показывается понятное сообщение вместо `undefined`
---
## [v1.10.0] - 2026-07-22
### 🧭 Навигация и инстансы
- **Командная палитра (Ctrl+K)** — палитра с поиском поверх общего реестра пунктов навигации (со своим store), позволяет перейти на любую страницу с клавиатуры
- **Единая навигация** — боковое меню и командная палитра работают от одного общего реестра nav-элементов
- **Расширенное меню инстанса** — в меню строки инстанса добавлены **Параметры**, **Мигрировать**, **Изменить ресурсы**, **Создать резервную копию** (новый `BackupDialog`) и **Удалить**; меню больше не закрывается при каждом живом обновлении списка (стабильные `columns` + `getRowId`)
- **Реорганизация вкладок инстанса** — для VM «Вычислительные ресурсы», «Опции CPU», «Управление дисками» и «Изменение размера диска» перенесены со вкладки **Настройки** на **Оборудование**; у LXC (где вкладки «Оборудование» нет) они остаются в **Настройках**. Общие карточки вынесены в `ResourceCards.tsx`
- **Новые общие UI-примитивы** — `alert`, `breadcrumb`, `checkbox`, `progress`, `switch`, `textarea`; ключи локализации RU/EN для новых пунктов меню и бэкапа
### 🛒 App Store
- **Глобальный трей свёрнутых операций** — свёрнутые долгие операции (установка приложения, сборка золотого шаблона) теперь живут в глобальном zustand-сторе (persist в `localStorage`) и рендерятся треем `MinimizedOpsTray` в layout приложения, а не самим диалогом:
- плашка **переживает навигацию между страницами и перезагрузку вкладки**; трей опрашивает прогресс сам (REST, каждые 3 с) независимо от диалога
- клик по плашке возвращает на страницу операции и разворачивает диалог — установка продолжается в `InstallWizard` (resume-режим через navigation state), шаблон открывает диалог золотого шаблона с предвыбранным сервером
- «Свернуть» добавляет операцию в трей и закрывает диалог; крестик на плашке только убирает плашку (операция продолжается на сервере); плашки несуществующих операций (404 после перезагрузки) трей убирает сам
- **Файлы данных приложения** — файлы, поставляемые вместе с приложением каталога (`data_files`), доставляются до `compose up`; учитываются `APP_PASSWORD` / `default_credentials` для Umbrel
- **Сворачиваемые диалоги установки/сборки** — долгие операции можно свернуть через `MinimizableDialog`
- **Надёжность установки/удаления**:
- **teardown** — отсутствующий в Proxmox CT считается удалённым, поэтому `orphaned`-приложение теперь можно удалить из БД
- **install** — поиск VMID сдвигается мимо занятых записей (`nextid` не резервирует номер, из-за чего retry мог подхватить чужой контейнер)
- **retry** — параметры установки (`cores/memory/disk/storage/bridge/ip_config/port`) сохраняются в `installed_apps.install_params` (JSONB, новая миграция) и переиспользуются при повторе вместо жёстких дефолтов
- **reconcile** — недоступный сервер больше не помечает все свои приложения `orphaned`
- **update** — реально опубликованный порт фиксируется в БД (`url`/health)
- **.env** — значения с `#` кавычатся (dotenv-парсер обрезал их как inline-комментарий)
- порт `80`, выбранный явно в мастере, публикуется как есть
- `useCatalog` — `available_only` включён в query key (устранена коллизия кэша)
### 🔒 Изоляция по владельцу
- **Живые ресурсы Proxmox теперь фильтруются по владельцу** — список инстансов (`/api/virtual-machines`) уже фильтровался по `owner_id`, но эндпоинты живых ресурсов `/api/resources/all` (дашборд) и `/api/{server_id}/resources` (страница ноды/сервера) отдавали **все** VM/LXC. Обычный пользователь (`proxmox.view`) через страницу ноды видел чужие инстансы. Оба эндпоинта теперь фильтруют VM/LXC по владельцу для непривилегированных (сверка `vmid` с кэшем `vm_instances`); админы и роли с `vm:manage` по-прежнему видят всё
- **`assign_instances_owner.py`** — скрипт массового назначения владельца существующим инстансам с `owner_id = NULL` (синхронизированным из Proxmox до появления владения)
### 🐛 Исправления
- **Очистка кэша React Query при login/logout** — при входе под другим пользователем в той же вкладке React Query больше не отдаёт закэшированные данные предыдущего пользователя (список инстансов и др.) до жёсткой перезагрузки. `queryClient` вынесен из `App.tsx` в `lib/query-client.ts` (доступен вне React), а `auth-store` вызывает `queryClient.clear()` при успешном входе и при выходе
---
## [v1.9.1] - 2026-07-17
### 🛒 App Store
- **Золотой шаблон отдельно на каждый сервер** — золотой vztmpl теперь хранится и разрешается по `server_id` Proxmox, а не одним глобальным указателем; сборка на одном сервере больше не перезаписывает/ломает шаблон, настроенный для другого. `APPSTORE_GOLDEN_TEMPLATE` теперь только последний запасной вариант для серверов без собственной сборки. Миграция 37 добавляет `app_operations.server_id`
- **Исправление сборки золотого шаблона** — автоматическая сборка (**App Store → золотой шаблон**) больше не падает на финальном шаге экспорта `vzdump` (`unable to parse directory volume name 'vztmpl/probe'`)
- **Надёжность DNS** — LXC приложений App Store (и CT сборки золотого шаблона) теперь получают явный `nameserver` (`APPSTORE_NAMESERVER`, по умолчанию `1.1.1.1 8.8.8.8`) вместо наследования резолвера ноды — это устранило таймауты при загрузке образов на нодах, чей DNS доступен только с хоста (например, через Tailscale)
- **Права bind-mount** — директории данных из `volumes:` приложения (`${APP_DATA_DIR}/...` или относительные пути) заранее создаются перед `up`, что устраняет `Permission denied` для контейнеров, пишущих под non-root пользователем (например, MariaDB)
- **Устранена гонка удаления/переустановки** — удаление приложения теперь ждёт фактического подтверждения Proxmox, что контейнер удалён (до 30 мин, покрывает медленную очистку диска), прежде чем освободить VMID, вместо предположения об успехе после короткого таймаута
- **Retry блокировки `pct create`** — временные ошибки «can't lock file ... got lock» сразу после удаления теперь повторяются несколько раз, а не роняют установку
- **Полный вывод ошибки** — упавшие шаги пайплайна теперь показывают последние 4000 символов вывода команды (раньше первые 2000), так что реальная причина не теряется под шумом прогресса `docker compose pull`
- **UI ошибки установки** — блок ошибки в диалоге установки теперь переносит длинный неразрывный текст (дайджесты образов, пути) и скроллится в своих границах вместо растягивания диалога
---
## [v1.9.0] - 2026-07-09
### 🌐 IPAM
- **Сети привязаны к серверу/ноде и рабочему пространству** — сеть IPAM теперь можно привязать к конкретному серверу/ноде Proxmox и к рабочему пространству; каждое пространство использует одну подсеть, а сеть автоматически выбирается активным пространством в мастерах создания инстанса и установки App Store, так что в типичном случае пользователю больше не нужно выбирать сеть вручную
- **Автовыбор в мастерах** — мастера развёртывания определяют целевую сеть/пул из текущего контекста и подставляют их; отсутствие привязки откатывается к ручному выбору
- Миграция: столбцы привязки сети IPAM к рабочему пространству + флаг «по умолчанию» через `backend/migrations/migrations.py`
### 🔧 Ноды
- **Домен/FQDN в поле хоста Proxmox** — форма добавления/редактирования сервера Proxmox теперь принимает доменное имя или FQDN (не только IP), которое резолвится при подключении
---
## [v1.8.1] - 2026-07-09
### 🛒 App Store
- **Umbrel как второй источник каталога** — каталог теперь мультиисточниковый: наряду с `runtipi/runtipi-appstore` можно включить `getumbrel/umbrel-apps` (~380 приложений). Источники настраиваются через `APPSTORE_SOURCES` (через запятую, по умолчанию `runtipi`); id приложений Umbrel неймспейсятся (`umbrel-<id>`), чтобы каталоги не конфликтовали
- **Compose-адаптер Umbrel** — установка понимает манифесты Umbrel: порт контейнера берётся из сервиса `app_proxy` (`APP_PORT`), runtime-сервисы Umbrel (`app_proxy`, `tor`) вырезаются, повисшие `depends_on` очищаются, а порт основного сервиса публикуется на хосте LXC, чтобы health-check прошёл; поведение Runtipi не меняется
- **Иконки Umbrel** — иконки берутся из отдельного репозитория `getumbrel/umbrel-apps-gallery` (`<app-id>/icon.svg`) при синке и кэшируются как логотипы Runtipi; сбой загрузки галереи никогда не роняет синк каталога
- **Мультиисточниковый синк** — каждый источник синкается независимо (сетевой сбой одного не отменяет другие), а проход *исчез → недоступен* ограничен своим источником, так что синк Runtipi никогда не трогает строки Umbrel
- **Фильтр источника и бейдж (UI)** — App Store получает фильтр по источнику и бейдж источника на карточке, показываемые только когда включено больше одного источника; метаданные каталога теперь отдают доступные `sources`
- **Кастомный порт установки** — мастер установки принимает произвольный порт хоста (валидация 1..65535), переопределяющий дефолт каталога
- **Мгновенная чистка инстанса** — удаление приложения сразу мягко удаляет связанный VM-инстанс для отзывчивости UI
- **Модель данных и настройки** — столбец `catalog_apps.source` (миграция 34); новые env-настройки `APPSTORE_SOURCES`, `UMBREL_APPSTORE_REPO`, `UMBREL_APPSTORE_REF`, `UMBREL_APPSTORE_GALLERY_REPO`
---
## [v1.8.0] - 2026-07-07
### 🛒 App Store
- **Каталог self-hosted приложений с установкой в один клик** — новый модуль App Store: каждое приложение разворачивается в собственном **unprivileged LXC**-контейнере с **Docker Compose** (1 приложение = 1 LXC). Каталог импортируется из репозитория `runtipi/runtipi-appstore` (~265 приложений проходят фильтр)
- **Catalog Service** — импорт метаданных, логотипов, описаний и compose-файлов в новую таблицу `catalog_apps`; идемпотентная синхронизация (кнопка *Обновить каталог* + автоматически раз в 24 ч через APScheduler); битый `config.json` пропускается, не роняя весь синк; флаг `update_available` вычисляется по `tipi_version`
- **Пайплайн установки** — клон золотого LXC-шаблона → `pct push` compose/`.env` → `docker compose up -d` → HTTP health-check, с **живым журналом шагов по WebSocket**, динамическим рендером `form_fields` (text/password/email/number/boolean/fqdn), автогенерацией `random`-секретов и хранением env в зашифрованном виде (Fernet); сгенерированные учётные данные показываются один раз
- **Управление жизненным циклом** — start / stop / restart / логи (`docker compose logs`) / удаление, плюс фоновая **реконсиляция состояния** каждые 60 с (контейнеры, удалённые мимо PVEmanager, помечаются `orphaned`)
- **Обновление и откат** — при обновлении создаётся pre-update снапшот Proxmox, затем `docker compose pull && up -d` с health-check; откат восстанавливает снапшот (вместе с данными); хранится только последний pre-update снапшот
- **UI** — сетка каталога (поиск, категории), страница приложения с дисклеймером об источнике и превью compose, мастер установки с расширенными параметрами LXC и живым прогрессом, экран управления **Мои приложения**; локали RU/EN; атрибуция *Powered by Runtipi appstore*
- **Безопасность** — только unprivileged LXC; `pct exec` / `pct push` через SSH с доставкой файлов через base64 и экранированием значений `.env` (без shell-инъекций); новые права RBAC `app:view` / `app:install` / `app:manage`
- **Модель данных и настройки** — таблицы `catalog_apps`, `installed_apps`, `app_operations` (миграции 31–32); новые env-настройки `APPSTORE_GOLDEN_TEMPLATE`, `RUNTIPI_APPSTORE_REF`, `APPSTORE_DATA_DIR`, `CATALOG_SYNC_INTERVAL_HOURS`, `APPSTORE_HOST_ARCH`
- **Документация** — `docs/golden-template.md` (подготовка золотого LXC-шаблона) и `docs/appstore-poc.md` / `appstore-catalog.md` / `appstore-engine.md`
---
## [v1.7.0] - 2026-06-26
### 🎯 Квоты
- **Квоты ресурсов на пользователя** — ограничение количества инстансов и суммарных vCPU, RAM и диска по всем VM/LXC пользователя. Отсутствие записи квоты или значение `NULL` в столбце означает *безлимит* по этой метрике, поэтому администраторы и пользователи без ограничений никогда не блокируются
- Бэкенд: новая модель `UserQuota` (таблица `user_quotas`) и `quota_service` (`get_user_usage` / `check_quota`); использование вычисляется из кэша `vm_instances` (`owner_id`) — учитываются только не удалённые и не шаблонные инстансы; при превышении лимита развёртывание VM/LXC прерывается с **HTTP 429**
- Права: новые RBAC-разрешения `quota:view` и `quota:manage` (категория *Управление пользователями*, оба требуют `user:view`)
- API: `GET/PUT /api/users/{id}/quota` (админ) возвращает лимиты вместе с текущим использованием; `GET /api/quota` (self-service) возвращает собственные лимиты и использование текущего пользователя
- Фронтенд: диалог квот с полосами использования на странице Пользователей (админ); карточка собственных лимитов на странице Настроек; подсказка об остатке квоты на шаге конфигурации в мастере создания инстанса; хуки `useUserQuota` / `useSetUserQuota` / `useMyQuota`; локали RU/EN
- Миграция: таблица `user_quotas` добавлена через `backend/migrations/migrations.py`
---
## [v1.6.0] - 2026-06-23
### ✨ Images
- **Каталог облачных образов** — просмотр и скачивание образов ОС из нескольких источников (стандартное зеркало Proxmox и кастомные зеркала); образы загружаются на хранилище Proxmox
- **Автоматическая конвертация в шаблон VM** — опция автоматической конвертации загруженного `.qcow2` образа в шаблон VM Proxmox после завершения скачивания
- **Кастомные зеркала для администраторов** — интерфейс, доступный только администраторам, для добавления, редактирования, просмотра и удаления источников-зеркал для поиска образов
- **Фильтрация образов по архитектуре** — образы фильтруются по архитектуре платформы узла (x86-64, aarch64); дефолт-архитектура соответствует платформе выбранного узла
- **Загрузка образов с прогрессом** — диалог загрузки с отслеживанием прогресса в реальном времени и опциональным переключателем конвертации в шаблон
### 🐛 Исправления
- **Нормализация расширения файла при импорте образа** — расширение `.img` автоматически нормализуется в `.qcow2` при импорте
- **Санитизация имени VM при импорте образа** — имена VM санитизируются при импорте образа для предотвращения ошибок создания; улучшенные сообщения об ошибках для прозрачной обратной связи с пользователем
---
## [v1.5.2-1] - 2026-06-20
### 🖥️ Интерфейс
- **Стилизованные диалоги подтверждения** — все нативные браузерные `confirm()` заменены единым тематическим модальным окном (`ConfirmDialog`). Добавлен глобальный `ConfirmDialogProvider` + хук `useConfirm` (Promise-based API). Затронуто: обновление панели, удаление шаблона/сервера/хранилища/SSH-ключа/HA-ресурса/SDN-зоны/VNet/подсети/IPAM-пула/аллокации, применение/откат сетевых изменений, очистка IPAM-сирот (16 мест в 11 файлах).
---
## [v1.5.2] - 2026-06-20
### 📊 Метрики
- **Реальное время: заполнение дисков VM, IOPS и сетевой I/O** — живые данные по каждому диску, скорости чтения/записи и сетевого ввода/вывода добавлены на вкладку Overview инстанса, в список инстансов и виджет топ-нагруженных на дашборде
- Backend: `get_vm_fsinfo()` запрашивает данные файловых систем через QEMU guest agent; `metrics_broadcaster` вычисляет скорости I/O (байт/с) из накопленных счётчиков; цикл обновления списка инстансов теперь опрашивает каждую запущенную VM через `status/current` (гранулярность 1 с) вместо `cluster/resources` (10 с от pvestatd), с ограниченным параллелизмом и ритмом 1 с
- Frontend: карточки по каждому диску, карточки Network I/O и I/O-rate на OverviewTab через WebSocket; колонки Net и Disk I/O в InstancesPage; Disk% отображается только для LXC; метрики в реальном времени на виджете топ-нагруженных на DashboardPage
### ⚙️ Настройки
- **Язык интерфейса для каждого пользователя** — у каждого аккаунта теперь есть собственная колонка `language` (миграция 26); переключатель языка перенесён с вкладки «Панель» на вкладку «Профиль»; язык сохраняется в БД и применяется через `i18n.changeLanguage()` сразу при сохранении
- **Улучшения вкладки «Панель»** — поле `panel_name` только для чтения (берётся из env `PANEL_NAME`); поле `log_retention_days` доступно в UI; API панели принимает `panel_name` в `PUT`, что позволяет переопределить значение через БД без правки env
### 🐛 Исправления
- **Поля настроек SMTP/безопасности** — переименованы поля запроса/ответа SMTP (`host`, `port`, `username`, `password`, `from_email`, `tls`); хук настроек безопасности переведён на `/admin/api/security/settings` с типизированным `SecuritySettings`; добавлены i18n-ключи `minutes` / `security_saved`
- **UX формы уведомлений** — добавлены тосты успеха/ошибки для кнопок сохранения SMTP и Telegram; переключатель языка в «Профиле» подключён; поля смены пароля очищаются только при успешном сохранении
- **Telegram — раздел администратора** — убрано поле Chat ID из глобальных настроек Telegram (оно никогда не сохранялось в БД); проверка теперь выполняется через личный раздел «Мои уведомления → Отправить тест»
- **Поле Webhook URL** — убрано нереализованное поле Webhook URL из настроек уведомлений, чтобы не вводить пользователей в заблуждение
---
## [v1.5.1] - 2026-06-04
### 🐛 Исправления
- **Сборка фронта: отсутствует компонент LogsPage** — в `src/router.tsx` есть ссылка на `./features/logs/LogsPage.tsx`, но сам файл был пропущен, из-за чего Vite 8.0.5 / rolldown не мог разрешить dynamic import. Добавлен `frontend/src/features/logs/LogsPage.tsx` со stat-карточками (всего за 24ч, ошибки, неудачные логины, записей на странице), фильтром по уровню, поиском, пагинацией и строками i18n. Использует уже существующие хуки `useLogs` / `useLogStats` / `useLogLevels`.
- **Сборка фронта: коллизия в `.gitignore`** — в `frontend/.gitignore` правило `logs` без якоря матчило и корневой `logs/`, и `src/features/logs/`, скрывая новую страницу из VCS. Правило ограничено корнем — `/logs`.
### 📚 Документация
- WIKI: версия поднята до v1.5.1, добавлен раздел **Logs & Audit** с описанием страницы, цветами уровней логов, ссылками на API (`/logs/api/logs`, `/logs/api/stats`, `/logs/api/levels`, `/logs/api/categories`), параметрами запросов, форматом ответа и заметками по правам доступа
- README: обновлён бейдж версии на 1.5.1
---
## [v1.5.0] - 2026-05-07
### 🐧 LXC — развёртывание из CT-шаблонов
- **Создание LXC из CT-шаблонов** — полный рабочий процесс создания LXC-контейнеров напрямую из CT-файлов Proxmox (`.tar.zst`, `.tar.gz`); выбор узла/хранилища, интеграция с IPAM, поддержка SSH-ключей
- **Единый мастер создания инстансов** — переработанный мастер (`CreateInstanceWizard`) поддерживает как VM-шаблоны (QEMU), так и CT-шаблоны (LXC) в едином многошаговом потоке: выбор типа, браузер шаблонов, конфигурация, подтверждение
- **Переустановка LXC из CT-шаблона** — поддержка переустановки контейнеров, созданных из CT-шаблонов; имя хоста, пароль, SSH-ключи и IP-адрес сохраняются при переустановке
- **Сохранение учётных данных и IP при переустановке LXC** — пароль root, SSH-ключи, IP-конфигурация и шлюз переносятся автоматически при переустановке контейнера
- **nesting=1 по умолчанию** — все новые LXC-контейнеры создаются с включённым `nesting=1` для обеспечения совместимости с systemd 255+ (cgroup v2)
- **Исправлено: двойное URL-кодирование SSH-ключей** — SSH-ключи, передаваемые в API Proxmox при создании LXC, больше не кодируются дважды
### 🔑 Управление SSH-ключами
- **API SSH-ключей** — полный CRUD для библиотеки SSH-ключей пользователя: `GET /api/ssh-keys`, `POST /api/ssh-keys`, `PUT /api/ssh-keys/{id}`, `DELETE /api/ssh-keys/{id}`
- **Управление ключами администратором** — `GET /api/ssh-keys/user/{user_id}` — администраторы могут просматривать и управлять SSH-ключами любого пользователя
- **Отпечаток ключа** — SHA-256 отпечаток вычисляется и сохраняется автоматически при создании/обновлении ключа
- **Хранение приватного ключа** — опциональное зашифрованное хранение приватного ключа рядом с публичным
- **UI управления SSH-ключами** — новая вкладка на странице настроек для добавления, редактирования и удаления SSH-ключей; отображает имя, отпечаток, комментарий и дату создания
- **Выбор ключа в мастере** — при развёртывании VM или LXC можно выбрать SSH-ключи из личной библиотеки; администраторы, создающие инстанс от имени другого пользователя, могут выбирать из ключей этого пользователя
- **Изоляция по пользователю** — обычные пользователи управляют только своими ключами; пользователи с разрешением `users.manage` могут управлять ключами любого аккаунта
### 🗑️ Операции с VM/LXC
- **Автоматическая остановка перед удалением** — если VM или LXC-контейнер запущен в момент запроса удаления, он автоматически останавливается; предотвращает ошибку Proxmox `can't lock file` при уничтожении
### 🖥️ Улучшения фронтенда
- **InstancesPage** — обновлённый макет, улучшенные бейджи статусов и меню действий
- **NodesPage** — обновлённые карточки узлов с более чистым отображением метрик
- **TemplatesPage** — улучшенный список шаблонов с лучшей фильтрацией и управлением группами
- **BackupsPage** — исправлены граничные случаи в списке резервных копий, диалоге восстановления и UI планировщика заданий
- **UsersPage** — улучшенная таблица пользователей с лучшим отображением ролей и индикаторами назначения серверов
- **SettingsPage** — добавлен раздел SSH Keys Manager; общая полировка UX
- **`api-client.ts`** — добавлены методы API SSH-ключей и эндпоинты CT-шаблонов
- **`websocket.ts`** — улучшение стабильности соединения
- **`types/index.ts`** — добавлены типы `SSHKey`, `LXCTemplate`, `LXCDeployRequest`
### 🛠️ Режим разработки
- **Dev mode** — новый оверрайд `compose.dev.yml` для Vite HMR (горячая замена модулей); использовать `pve dev` или `docker compose -f compose.yml -f compose.dev.yml up -d frontend`; исходные файлы монтируются как тома, изменения применяются мгновенно без пересборки
- **Маркер `.dev-mode`** — сигнализирует CLI `pve` о том, что фронтенд запущен в режиме разработки
---
## [v1.4.0] - 2026-04-06
### 🌐 Сети — управление SDN и интерфейсы узлов
- **Страница сетей** — новая объединённая страница (`/proxmox/networks`) для управления зонами SDN, VNet, подсетями и сетевыми интерфейсами на уровне узла; добавлена в боковую навигацию с специальной иконкой
- **Редактирование зоны SDN** — `PUT /api/servers/{server_id}/sdn/zones/{zone}` — обновление свойств зоны (mtu, dns, reversedns, ipam и т.д.)
- **Редактирование VNet SDN** — `PUT /api/servers/{server_id}/sdn/vnets/{vnet}` — обновление alias, VLAN tag, vlanaware и т.д.
- **Удаление подсети SDN** — `DELETE /api/servers/{server_id}/sdn/vnets/{vnet}/subnets/{subnet_cidr}` — удаление подсети; опционально удаление связанной сети IPAM (`?delete_ipam_network=true`)
- **Автоматическое создание сети IPAM при создании подсети** — `POST .../subnets` теперь поддерживает `create_ipam_network: true`; создаёт соответствующую `IPAMNetwork`, связанную с сервером и VNet, или возвращает существующую
- **Сетевые интерфейсы узлов** — полный CRUD для интерфейсов на уровне узла (мосты, связки, VLAN, ethernet): список, получение, создание, обновление, удаление через `proxmox_client.py`
- **Применение / Откат конфигурации сети узла** — `PUT` и `DELETE` на `/nodes/{node}/network` для активации или отката ожидающих изменений
- **API получения списка узлов** — `GET /api/servers/{server_id}/nodes` — возвращает узлы для сервера Proxmox (используется селекторами страницы сетей)
### 🧹 Очистка кода
- **Очистка импортов модуля SDN** — удалены неиспользуемые импорты (`ssl`, `asyncio`, `httpx`, `websockets`, `func`, `List` и т.д.) из `sdn.py`
- **Маршрутизатор сетей** — новый модуль `networks.py` под `api/proxmox/` с маршрутом HTML страницы и API для узлов; зарегистрирован в `proxmox/__init__.py`
### 🌍 i18n
- **49 новых ключей переводов** (EN + RU) для страницы сетей: типы интерфейсов, поля bridge/bond/VLAN, применение/откат конфигурации, метки интеграции IPAM, редактирование зоны/vnet, удаление подсети и многое другое
---
## [v1.3.1] - 2026-04-05
### 🔍 Список ВМ — фильтрация по узлам и адаптивная панель инструментов
- **Фильтр по серверу** — новое выпадающее меню для фильтрации списка ВМ по серверу/кластеру Proxmox; заполняется динамически из загруженных ВМ
- **Фильтр по узлу** — новое выпадающее меню для фильтрации по узлу Proxmox; автоматически сужает список узлов, принадлежащих выбранному серверу
- Оба фильтра сохраняются в параметры URL (`?server=...&node=...`) и восстанавливаются при перезагрузке страницы
- **Панель инструментов переделана в 2 строки** — строка 1: все фильтры (тип / статус / сервер / узел / поиск с иконкой + статистика); строка 2: чекбокс выбрать все, пагинация, кнопки действий
- **Адаптивные точки разрыва** — при ≤900px метки кнопок скрыты; при ≤768px фильтры переносятся 2 в строку; при ≤480px каждый фильтр во всю ширину; счётчик статистики скрыт на мобильных
- Вспомогательные функции `populateServerFilter()` и `populateNodeFilter()` заполняют выпадающие меню из загруженных данных ВМ
- Панель инструментов снимков мигрирована с устаревших классов `.vm-toolbar-left/.vm-toolbar-right` на inline flex, не затронута переделкой панели инструментов
---
## [v1.3.0] - 2026-03-27
### 🖥️ Консоль — исправления VNC и терминала
- **VNC «Connection closed» исправлено** — `RFBClass` (noVNC) теперь получает строку URL WebSocket непосредственно вместо предварительно открытого объекта `WebSocket`; хак `onFirstMessage`, нарушавший квитирование RFB перед первым фреймом, полностью удалён из `virtual_machines.html` и `instance_detail.html`
- **VNCAuth исправлено для QEMU** — `vncproxy` теперь вызывается с `"generate-password": 1`; возвращаемый 8-символьный DES ключ передаётся в noVNC как пароль VNC; возвращается к `ticket` при отсутствии `password` (режим NoAuth Proxmox 9.x)
- **LXC `vncproxy` 500 исправлено** — LXC API `vncproxy` не принимает `generate-password`; параметр пропускается для LXC, который использует `ticket` как пароль VNC
- **Контекстное меню LXC открывает терминал xterm.js** — правый клик в списке ВМ теперь показывает **Терминал** для типа `lxc` (вызывает `openTerminalConsole()`) и **Консоль VNC** для типа `qemu`; ранее все типы использовали `openVNCConsole()`
- **Модальное окно терминала добавлено на страницу списка ВМ** — `#terminalModal` с полным xterm.js (FitAddon, WebLinksAddon, тема Dracula) добавлено в `virtual_machines.html`; переиспользует существующие API endpoints `GET /api/{server_id}/container/{vmid}/terminal` и `WebSocket /proxmox/ws/terminal/{server_id}/{node}/{vmid}`
---
## [v1.2.0] - 2026-03-16
### 🐛 Исправление ошибки — конфликт блокировки при изменении размера диска
- **Основная причина исправлена** — `resize.put()` в API Proxmox асинхронна и возвращает задачу UPID; старый код не ждал завершения этой задачи перед запуском ВМ, что заставляло Proxmox сообщать `can't lock file '/var/lock/qemu-server/lock-xxx.conf' — got timeout` и `command '/usr/bin/qemu-img resize' failed: got timeout`
- **`configure_vm` (создание ВМ из шаблона)** — теперь захватывает UPID изменения размера и вызывает `wait_for_task(120 s)` перед возвратом; ВМ может быть запущена только после полного изменения размера диска
- **`resize_vm_disk`** — то же исправление применено; задача изменения размера завершается перед возвратом функции
- **`resize_container_disk` (оба метода LXC)** — то же исправление; перезапуск контейнера выполняется только после подтверждения завершения задачи изменения размера
- **Удалено ненужное перезагрузка ВМ после изменения размера** — изменение размера диска QEMU происходит на уровне хранилища и не требует перезагрузки ВМ; автоматический вызов `restart_vm()` после ручного изменения размера удалён, чтобы предотвратить второй конфликт блокировки
---
## [v1.1.8] - 2026-03-16
### 👤 Назначение пользователю серверов
- **Таблица `user_servers`** (миграция 20) — новая таблица связей `User ↔ ProxmoxServer`; позволяет напрямую назначать конкретные серверы пользователям
- **API `GET /api/users/{id}/servers`** — список серверов, назначенных пользователю
- **API `GET /api/users/{id}/server-assignments`** — расширенный список всех серверов с флагами `assigned` и `compatible` (совместимость по рабочим областям)
- **API `PUT /api/users/{id}/servers`** — установка набора серверов для пользователя; проверяет пересечение рабочих областей — если сервер и пользователь не состоят ни в одной общей рабочей области, возвращает `409 workspace_conflict`
- **Фильтрация серверов для обычных пользователей** — пользователь видит только серверы, которые ему назначены **и** входят в его рабочую область
### 🖥️ Владелец ВМ / LXC
- **API `GET /api/{server_id}/vm/{vmid}/owner`** — получить текущего владельца ВМ/LXC и список доступных пользователей
- **API `PUT /api/{server_id}/vm/{vmid}/owner`** — назначить или снять владельца (только для админ); действие логируется в аудит
### 🔐 RBAC и роли
- **Миграция 21** — `dashboard:view` принудительно выставлен в `false` для роли `user` (новый формат ключей)
- **Миграция 22** — ключ `proxmox.cluster.manage` → `cluster:manage` в роли `admin`; старый dot-формат удаляется, чтобы не сбрасывать весь карту разрешений
- **`user_count`** в ответе `GET /api/roles` — для каждой роли возвращается количество активных пользователей
### 🚀 Умное перенаправление после входа
- После успешного входа производится запрос `GET /api/auth/me` — пользователь автоматически перенаправляется на первую страницу, к которой у него есть доступ (`/dashboard` → `/virtual-machines` → `/vms` → `/backups` → `/ipam` → `/logs` → `/settings`)
- Поддержка параметра `?returnUrl=` — возврат на запрошенную страницу после входа
### 🔧 Прочие улучшения
- `GET /api/servers` для непривилегированного пользователя объединяет фильтры `assigned_servers` и `WorkspaceServer` — пользователь видит только серверы из своей рабочей области, которые ему назначены
- `node_uptime` добавлен в ответ детального API сервера
- Удалена лишняя проверка `PermissionChecker` на GET-маршруте страницы `/users` (HTML)
---
*Для истории более ранних версий см. английскую версию [CHANGELOG.md](CHANGELOG.md)*