Commit Graph

145 Commits

Author SHA1 Message Date
markmorado
e62626c79b fix(nodes): детерминированная сортировка списка серверов
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 1m0s
Deploy to Production / Trigger production update (push) Successful in 7s
Список серверов терял порядок при фоновом рефетче (каждые 30с),
т.к. запрос с WHERE id IN (...) не гарантирует стабильный порядок строк.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 00:40:17 +05:00
markmorado
3424823793 feat(cluster): режим eject "только панель" для нод, не входящих в кластер
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 56s
Deploy to Production / Trigger production update (push) Successful in 7s
При провале join нода остаётся стейл-записью с cluster_name в панели, хотя
в Proxmox она standalone. Обычный eject пытается pvecm delnode через SSH и
падает ("SSH connection failed"), т.к. нода не член кластера и/или узел
недоступен по SSH.

- eject-эндпоинт: новый параметр panel_only — отвязывает панельную запись
  (cluster_name=None) без обращения к Proxmox/SSH.
- UI: чекбокс "Только отвязать запись в панели" в диалоге исключения,
  блокирует поле пароля при выборе.
- Локали ru/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 03:36:55 +05:00
markmorado
c88cdb4e26 fix(cluster): join по приватному адресу corosync + подтверждение членства
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 58s
Deploy to Production / Trigger production update (push) Successful in 7s
Проблема: присоединяемая нода подключалась к кластерной по её адресу из
панели (часто публичный FQDN), из-за чего join падал с
"Can't connect to <fqdn>:8006 (Connection timed out)" при выделенной
corosync-сети. Плюс панель помечала ноду как присоединённую сразу после
старта задачи, показывая ложное членство даже при провале join.

- join: новый опциональный параметр cluster_host — приватный IP кластерной
  ноды в corosync-сети; используется как hostname для API-подключения и
  для выбора fingerprint из nodelist. Фолбэк — ip_address из панели.
- join: cluster_name пишется в БД только после подтверждения, что нода
  реально появилась среди нод кластерной ноды (опрос до ~40с, укладываемся
  в nginx proxy_read_timeout 60с). Иначе — confirmed=false и предупреждение.
- UI: поле "Адрес кластерной ноды для подключения" в шаге 3 мастера,
  обработка неподтверждённого join (toast.warning, окно не закрывается).
- Локали ru/en.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 03:28:39 +05:00
markmorado
3b10e903a4 fix(cluster): корректное извлечение fingerprint из nodelist при join
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 56s
Deploy to Production / Trigger production update (push) Successful in 8s
GET /cluster/config/join не возвращает fingerprint на верхнем уровне —
он лежит в nodelist[].pve_fp. Добавлен хелпер _extract_fingerprint,
который выбирает pve_fp целевой ноды (по IP), с фолбэком на
preferred_node и первую ноду. Устраняет ошибку 502
"Не удалось получить fingerprint кластера" при присоединении нод.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 03:07:53 +05:00
markmorado
0d056a89a5 feat(nodes): TLS-сертификаты ноды и массовая миграция инстансов
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 57s
Deploy to Production / Trigger production update (push) Successful in 8s
- Сертификаты: методы клиента + endpoints GET/POST/DELETE
  /nodes/{node}/certificates, хуки и вкладка «Сертификаты» в
  NodeAdminManager (просмотр, загрузка PEM cert+key, удаление кастомного,
  бейдж срока действия)
- Bulk migrate: тип bulk_migrate в очереди задач с WS-прогрессом,
  target_node/online в BulkOperationItem, диалог выбора целевой ноды и
  кнопка в тулбаре инстансов (в пределах одного сервера/кластера)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 02:48:42 +05:00
markmorado
afde2bf569 feat(nodes): управление нодой как в Proxmox — питание, консоль, журнал/задачи/система
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 58s
Deploy to Production / Trigger production update (push) Successful in 7s
Ядро:
- Reboot/Shutdown ноды (POST /nodes/{node}/status) под новым правом node:power
  с подтверждением в UI; меню действий на карточке ноды + пункт «Консоль (Shell)».

Доп. вкладки в «Администрировании ноды»:
- Журнал (syslog ноды), История задач (tasks + лог задачи),
  Система (DNS / /etc/hosts / часовой пояс).

Бэкенд: методы node_power_action/get_node_syslog/get_node_tasks/get_node_task_log/
get_node_task_status/DNS/hosts/time в NodeAdminMixin + эндпоинты в node_admin.py.
RBAC: право node:power (admin + backfill server:manage).

Проверено: py_compile, tsc --noEmit, vite build; live — маршруты в openapi,
403 без токена, node:power засеян роли admin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 02:19:07 +05:00
markmorado
629357a0ee feat(appstore): автосборка золотого LXC-шаблона из UI (Workstream B)
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 57s
Deploy to Production / Trigger production update (push) Successful in 8s
Автоматизирует ручную процедуру docs/golden-template.md: pveam download
базового Debian → pct create временного CT → установка Docker/Compose →
каталоги/сеть/logrotate → (опц.) проверка nginx → vzdump в vztmpl → удаление
временного CT. При сбое недособранный CT очищается.

- backend/app/appstore/golden_template.py — сборщик поверх SSH-инфраструктуры
  пайплайна (pct/pvesm/pveam/vzdump); идемпотентность по существующему vztmpl,
  версия базового шаблона берётся из pveam (не хардкодится)
- appstore_engine: prepare_golden_template/_run_golden_template (фон, журнал
  AppOperation + WS), golden_template_status; собранный volid сохраняется в
  PanelSettings и предпочитается env при установке (_resolve_golden_template)
- API: POST /api/appstore/golden-template, GET .../golden-template/status
  (app:manage / app:view)
- Frontend: GoldenTemplateDialog (выбор сервера/ноды/хранилищ, force, живой
  прогресс опросом статуса) + admin-кнопка «Золотой шаблон» на странице App
  Store; хуки useGoldenTemplateStatus/useBuildGoldenTemplate; локали RU/EN

Проверено: роуты зарегистрированы, golden_template_status и приоритет
_resolve_golden_template (override→saved→env) корректны, tsc 0 ошибок.
Полный e2e-прогон сборки на реальной ноде — по запросу.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:53:47 +05:00
markmorado
aebb726231 perf(appstore): быстрый синк Umbrel — иконки с CDN параллельно + стартовый синк
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 56s
Deploy to Production / Trigger production update (push) Successful in 7s
A1: иконки Umbrel тянутся пофайлово с GitHub Pages CDN в пуле из 16 потоков
(<app-id>/icon.svg) вместо тяжёлого tarball галереи со скриншотами. Синк
Umbrel: ~7.5 мин → ~13-17с, 379/379 иконок. Настройка UMBREL_APPSTORE_GALLERY_CDN
заменяет UMBREL_APPSTORE_GALLERY_REPO. 404/сетевой сбой отдельного запроса
просто пропускает иконку, не роняя синк.

A2: на свежем деплое каталог пуст, а IntervalTrigger впервые сработает лишь
через сутки. Теперь при пустом каталоге планировщик ставит разовый синк
немедленно (trigger=date) — каталог наполняется на старте без ручной кнопки.

Проверено на стенде: стартовый синк наполнил каталог 644 (runtipi 265 +
umbrel 379) за 16.5с; эндпоинт логотипа отдаёт 200 image/svg+xml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:40:39 +05:00
markmorado
633f1a424c feat(appstore): кастомный порт при установке + мгновенная чистка инстанса
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 57s
Deploy to Production / Trigger production update (push) Successful in 7s
App Store:
- поле кастомного порта в мастере установки (переопределяет порт каталога)
- валидация диапазона порта 1..65535 (Pydantic Field + EngineError)
- APP_PORT в окружении синхронизируется с выбранным портом
- при удалении приложения инстанс сразу помечается deleted в кэше
  vm_instances (soft_delete_vm_instance), не дожидаясь фонового воркера

Также включены правки IPAM (привязка сетей к рабочей области, UI
WorkspaceSelect) и синхронизации каталога App Store.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:20:28 +05:00
markmorado
bbaae7c2b1 fix(nodes): принимать доменное имя/FQDN в поле хоста сервера Proxmox
Some checks failed
Deploy to Production / Trigger production update (push) Has been cancelled
Backend Tests / pytest (Python 3.12) (push) Has been cancelled
Поле ip_address валидировалось строго как IP, из-за чего подключение ноды по домену (node1.tzim.cloud) падало с 'Invalid IP address format'. Добавлен хелпер validate_host: принимает IP (v4/v6) или корректный hostname/FQDN. IPAM-валидаторы по-прежнему требуют строгий IP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:20:06 +05:00
markmorado
23d3f95f54 fix(appstore): выбор хранилища в мастере установки + видимость реальной ошибки
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 57s
Deploy to Production / Trigger production update (push) Successful in 8s
- Установка: поле «Хранилище» стало выпадающим списком доступных rootdir-хранилищ
  ноды (useLXCStorages) с автовыбором валидного вместо ручного ввода
- Диагностика сбоя: увеличен захват вывода упавшей команды (400 → 2000 символов)
  и добавлен 2>&1 к docker compose pull/up — реальная причина (напр. отсутствие
  тега образа) больше не обрезается за строками warning

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:28:39 +05:00
markmorado
6d5df8caa7 fix(appstore): устранить зависание/провал установки (compose network + SSH-таймаут)
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 56s
Deploy to Production / Trigger production update (push) Successful in 8s
- pipeline: _normalize_compose() объявляет упомянутые в сервисах сети
  (tipi_main_network) как external в top-level — иначе docker compose up
  падал с "refers to undefined network: invalid compose project"
- pipeline: LOCAL_DOMAIN=tipi.local в .env (стандартная переменная Runtipi)
- pipeline: per-command таймауты (_CMD_TIMEOUT/_DOCKER_TIMEOUT), при таймауте
  установка падает с ошибкой, а не зависает на 75%
- ssh_client: execute() ждёт готовности exit-status с жёстким дедлайном
  вместо бесконечного recv_exit_status() (ловушка paramiko)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 01:15:33 +05:00
markmorado
9bfb055f69 feat(appstore): показывать операции App Store в «Задачах» + IP из IPAM при установке
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 55s
Deploy to Production / Trigger production update (push) Successful in 8s
- AppOperation.to_task_dict(): операции install/update/delete/… приведены к
  общему формату и включены в /api/all-tasks, active-count и clear-completed
- Установка приложения: опциональный выбор сети/пула IPAM — статический IP
  вместо DHCP (net0 ip=<addr>/<mask>,gw=…), освобождение адреса при удалении
- Мастер установки: селекторы «Сеть IPAM»/«Пул IPAM» (ru/en), по умолчанию DHCP

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:20:33 +05:00
markmorado
e453f49a6a fix(appstore): удалять запись installed_apps при удалении инстанса со страницы инстансов
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 55s
Deploy to Production / Trigger production update (push) Successful in 7s
При удалении LXC/VM через delete_vm/delete_container чистим таблицу installed_apps
(по server_id+vmid), иначе приложение из каталога оставалось «хвостом» в «Мои приложения».
Шлём WS-эвент appstore_app_deleted для live-обновления списка у владельца.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:18:40 +05:00
markmorado
635947eb15 fix(appstore): рендер markdown/скриншотов, фильтр категорий, локализация ошибки, ширина карточек
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 57s
Deploy to Production / Trigger production update (push) Successful in 8s
- Описания приложений: безопасный рендер markdown + HTML (react-markdown +
  remark-gfm + rehype-raw + rehype-sanitize), скриншоты отображаются как картинки
- Фильтр по категории: переведён на надёжную фильтрацию в Python (JSONB-оператор
  на generic JSON-колонке отрабатывал ненадёжно)
- Ошибка отсутствия золотого шаблона локализована (i18n appstore.golden_missing)
- Каталог: карточки шире (макс 3 колонки вместо 4), описание в 3 строки

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:28:19 +05:00
markmorado
0606e8ac77 feat(appstore): модуль «App Store» — каталог self-hosted приложений (1 app = 1 LXC + Docker Compose)
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 1m4s
Deploy to Production / Trigger production update (push) Successful in 12s
Полный цикл M0–M5 по ТЗ:
- Catalog Service: импорт runtipi/runtipi-appstore → catalog_apps, идемпотентный
  синк, кэш логотипов, авто раз в 24ч (APScheduler), флаг update_available.
- App Engine: установка (клон золотого шаблона → pct push compose/.env →
  docker compose up → health-check) с журналом шагов, шифрованием env (Fernet),
  генерацией random-секретов и прогрессом по WebSocket.
- Lifecycle: start/stop/restart/logs/delete + реконсиляция состояния (orphaned).
- Update/Rollback через снапшоты Proxmox (pre-update, хранение последнего 1).
- UI: экраны App Store (сетка/поиск/категории), страница приложения, мастер
  установки (динамическая form_fields + advanced + прогресс + креденшелы),
  My Apps; i18n ru/en; атрибуция Runtipi.
- RBAC app:view/install/manage; только unprivileged LXC; pct exec/push без
  shell-инъекций (base64 + экранирование).
- Модели catalog_apps/installed_apps/app_operations (миграции 31–32).
- Docs: golden-template, appstore-poc/catalog/engine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:05:20 +05:00
markmorado
782524fff6 feat(hardware): проброс физических дисков ноды в VM (raw passthrough)
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 56s
Deploy to Production / Trigger production update (push) Successful in 7s
Добавлена возможность пробросить физический диск ноды в VM по стабильному
пути /dev/disk/by-id/ (scsiN/virtioN/sataN) в карточке «Проброс устройств».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:37:13 +05:00
markmorado
bec7b20e31 feat(auth): двухфакторная аутентификация (TOTP) для входа в панель
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 58s
Deploy to Production / Trigger production update (push) Successful in 8s
Бэкенд:
- two_factor_service: генерация секрета, otpauth-URI, QR как inline-SVG,
  одноразовые backup-коды (хранятся как SHA-256)
- эндпоинты /api/auth/2fa/{status,setup,enable,disable}; гейт 2FA в login
  (неверный код -> 400, чтобы SPA не сбрасывала форму); two_factor_enabled в /me
- админский сброс: POST /api/users/{id}/disable-2fa
- модель/схемы/init.sql + идемпотентная миграция №30 (two_factor_backup_codes)
- зависимости: pyotp, qrcode

Фронтенд:
- двухшаговый вход с полем кода (TOTP/backup)
- карточка 2FA в профиле: QR, проверка, backup-коды, отключение
- бейдж/сброс 2FA в списке пользователей; локали ru/en

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 21:08:38 +05:00
markmorado
fc42e8fc19 feat(nodes): показывать версию PVE, рабочие области и аптайм на карточках узлов
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 57s
Deploy to Production / Trigger production update (push) Successful in 8s
Карточки на странице «Узлы» дополнены важной информацией:
- версия Proxmox VE (чип «PVE x.y.z»);
- число нод в кластере (для кластерных серверов);
- аптайм узла;
- чипы рабочих областей, в которых состоит сервер.

Бэкенд: эндпоинт /api/{id}/nodes отдаёт version; ответ серверов
дополнен полями cluster_name и workspaces (ServerWorkspaceBrief).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 18:37:28 +05:00
markmorado
476b73b411 docs(metrics): актуализировать комментарии интервалов брокера
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 1m2s
Deploy to Production / Trigger production update (push) Successful in 8s
Реальные интервалы циклов: dashboard 2с, server 2с, instance 1с.
В комментариях было устаревшее 10с/5с. Логику не меняем — дашборд
уже обновляется раз в 2 секунды.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 18:30:56 +05:00
markmorado
b9e5ef9426 fix(dashboard): не обнулять узлы кластера при REST-рефетче и сбоях WS
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Successful in 56s
Deploy to Production / Trigger production update (push) Failing after 9s
Данные по узлам (nodes) приходят только из WebSocket dashboard_metrics,
а REST /api/resources/all их не содержит. Каждый 15-секундный рефетч REST
затирал ноды -> таблица узлов и счётчик обнулялись, WS возвращал через 2с.

- frontend: при REST-рефетче переносим последние известные nodes из кэша,
  чтобы node-таблица не мигала
- backend: кэш последнего удачного опроса по каждому серверу
  (_server_last_good) — транзиентный таймаут Proxmox больше не отдаёт
  пустые vms/containers/nodes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 00:56:08 +05:00
markmorado
d4c5c6110a fix(dashboard): устранить мигание показаний и поток ошибок 422
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Successful in 58s
Deploy to Production / Trigger production update (push) Failing after 10s
- поднять лимит /proxmox/api/all-tasks до 500 (дашборд запрашивал limit=500
  при потолке le=200, что давало 422 каждые 5с)
- обработчик WS dashboard_metrics больше не затирает кэш пустыми/частичными
  тиками: игнорирует пустые сводки и сохраняет последние валидные
  nodes/vms/containers при транзиентном сбое Proxmox

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 00:49:54 +05:00
markmorado
00e36e1cc6 feat: Proxmox management suite — hardware, firewall, pools, disks, storage, access, node admin
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 54s
Deploy to Production / Trigger production update (push) Successful in 7s
- VM/LXC hardware editor with PCI/USB passthrough (BIOS/OVMF + efidisk,
  machine type, VGA, SCSI controller, CPU sockets); passthrough locked while running
- Firewall at datacenter/node/VM levels: rules, security groups, IP sets,
  aliases, options, log — with rule dialog referencing security groups
- Resource pools (CRUD + membership) and per-instance tags editor
- Node disks: physical disk list, SMART viewer, ZFS pool create/destroy, wipe
- Storage: add LVM/LVM-thin/ZFS/RBD/CephFS types + enable/disable toggle
- PVE Access: API tokens (create with one-time secret, delete) and realms (LDAP/AD/OpenID)
- Node admin: systemd services (start/stop/restart), APT updates & repositories
- Shared: SelectTrigger defaults to full width (fixes narrow dropdowns)

Also bundles related in-progress work: auth screen refresh, node shell console
page, server-updates badge, and node:*/access:* RBAC permissions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 05:40:52 +05:00
markmorado
5c8baefb3f feat: add firewall, pools, access, disks, node admin and VM hardware management
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 1m0s
Deploy to Production / Trigger production update (push) Successful in 7s
Backend: new proxmox mixins and API routes for firewall, pools, access
control, node admin and disks; extend storage and VM endpoints.

Frontend: node managers (firewall, pool, access, disk, node admin), VM
firewall and hardware tabs, PCI/USB device dialogs, related hooks and
locale strings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 00:02:15 +05:00
markmorado
c12fbf13e5 feat(instances): add VM/LXC migration, autostart/boot order, protection
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 52s
Deploy to Production / Trigger production update (push) Successful in 7s
- Migrate VMs/LXCs between cluster nodes with live progress tracking
- Configure autostart, boot order, startup/shutdown delays, and delete protection from the instance settings tab
- Group OS templates by detected OS (Ubuntu, AlmaLinux, etc.) instead of a single "Cloud Images" bucket
- Expand native Proxmox backup job management (schedule, compression, retention, guest selection, notifications)
- Add ru/en translations for the above
2026-07-03 02:40:58 +05:00
91080d8574 feat(api): expose SDN DNS server entries
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 03:05:06 +05:00
2dff39ada0 feat(api): include SDN vnets in node networks listing
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:57:22 +05:00
161182b392 feat(api): support netN add/remove in VM and LXC config updates
- update_vm_config: whitelist netN/scsiN/virtioN/ideN/sataN/ipconfigN by
  regex instead of hardcoded 0-3; accept validated delete='netN' key;
  raise with real Proxmox error text instead of returning False
- update_container_config: refactor to dict signature with whitelist +
  netN + delete (fixes LXC config PUT endpoint that passed the whole
  dict as hostname)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:32:21 +05:00
7d52297f76 fix(deploy): apply VM config reliably after clone
- wait for clone lock release before config.put (cross-node clones kept
  lock past wait_for_task, dropping ALL requested settings silently)
- configure_vm applies params in independent groups (compute/network/
  cloudinit/disk) with per-group errors and lock retry
- set sockets=1 with cores so total vCPU matches request
- keep template net0 model/MAC/options, replace only bridge
- disk resize: pick boot disk via regex over all buses, report failures
- surface config errors as deploy warnings in UI toast instead of
  silent success

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:31:21 +05:00
35dcbfd18f feat(quotas): per-user resource limits for VM/LXC deployment
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 54s
Deploy to Production / Trigger production update (push) Successful in 8s
Add per-user quotas (max instances, vCPU, RAM, disk) enforced at deploy
time and surfaced to users self-service.

Backend:
- UserQuota model (one-to-one, NULL = unlimited) + migration #28
- quota_service: usage from vm_instances cache, check_quota raises 429
- enforce in VM (templates.py) and LXC (lxc.py) deploy paths
- RBAC quota:view / quota:manage, granted to admin
- admin API GET/PUT /admin/api/users/{id}/quota
- self-service GET /settings/api/quota
- tighten VM list isolation via can_view_all_instances

Frontend:
- admin quota dialog in UsersPage with usage bars
- quota card on Settings page (own limits)
- remaining-quota hint in CreateInstanceWizard config step
- useUserQuota/useSetUserQuota, useMyQuota hooks; RU/EN locales

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 18:15:02 +05:00
8b93161434 feat(auth): self-service password reset (forgot/reset endpoints)
Add an email-based "forgot password" flow:
- PasswordResetToken model (stores only a SHA-256 hash, single-use, TTL)
  and migration 27 for the password_reset_tokens table.
- POST /api/auth/forgot-password — generic response (no account
  enumeration), 60s throttle, emails a one-time reset link via the
  existing EmailChannel as a background task.
- GET /api/auth/reset-password/validate — token validity check for the UI.
- POST /api/auth/reset-password — validates the token, enforces the
  password policy, sets the new password, clears lock state, invalidates
  outstanding tokens and terminates all sessions.
- Configurable link lifetime via PASSWORD_RESET_TOKEN_EXPIRE_MINUTES (30m).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 03:24:19 +05:00
f880c9e177 fix(images): idempotent download (skip if file exists) + real failure reason
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 52s
Deploy to Production / Trigger production update (push) Successful in 8s
Retrying a template build failed at download because the qcow2 from a prior
attempt already existed and download-url refuses to overwrite. Now skip the
download when the volume already exists (volume_exists), and surface the PVE
task-log tail on download failure instead of a generic message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:38:50 +05:00
cdb49abf0f fix(images): sanitize VM name for import; honest create-error message
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 55s
Deploy to Production / Trigger production update (push) Successful in 9s
create_vm_with_import got the display name ('Ubuntu 22.04 LTS (Jammy) Cloud')
which PVE rejects (Parameter verification failed on 'name'). Sanitize to a
DNS-safe name (e.g. ubuntu-22-04-lts-jammy-cloud). Also stop attributing every
create failure to 'import unsupported' — only say that when the error mentions
import; otherwise surface the real error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:31:34 +05:00
1e06be10ad fix(images): normalize import filename extension (.img -> .qcow2)
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 52s
Deploy to Production / Trigger production update (push) Successful in 8s
Proxmox content=import rejects Ubuntu cloud '*.img' with 'invalid filename
or wrong extension' though the format is qcow2. Rename non-import-extension
files to .qcow2 before download-url; import-from auto-detects the format.
Applied to both download and template workers (download filename + import volid).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:52:56 +05:00
2dff9f8c5c feat(images): node-arch endpoint for arch-aware filtering
GET /proxmox/api/{server_id}/images/node-arch derives amd64/arm64 from
node status current-kernel.machine; returns null when node unreachable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:28:04 +05:00
ed84e00e7b feat(images): Phase 3 backend — custom mirror CRUD
GET/POST/PUT/DELETE /proxmox/api/images/mirrors (perm template:manage).
Enabled mirrors merge into the catalog feed automatically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:17:00 +05:00
c7edf72d19 feat(images): Phase 2 — auto-convert qcow2 to VM template
- client.create_vm_with_import: API-only disk import via scsi0 import-from (PVE 8.2+)
- _do_image_template_sync worker: download(import) -> create+import -> cloud-init
  -> convert_to_template -> register OSTemplate (Cloud Images group)
- images download endpoint: to_template branch + cloud-init/disk_storage params
- clear fallback error when storage lacks import support (file kept)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:11:20 +05:00
7aed5dd97e feat(images): backend catalog + mirror model + download API/worker
Phase 1 of VM image mirror feature:
- StorageMixin.download_url (PVE download-url/aria2) + get_download_target_storages
- builtin curated cloud-image catalog (Ubuntu/Debian/Rocky/Alma, amd64+arm64)
- ImageMirror model for admin custom mirrors
- /proxmox/api/images: catalog, lxc-templates proxy, storages, download
- _do_image_download_sync worker with log-tailing progress (DeployTask)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:51:21 +05:00
6e67ad7e77 fix(disk-resize): keep growpart script ASCII-only
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Successful in 51s
Deploy to Production / Trigger production update (push) Has been cancelled
Proxmox's guest-agent exec (PVE/QemuServer/Agent.pm) dies with
"Wide character in subroutine entry" when input-data contains non-ASCII
bytes. The growpart script had Cyrillic comments, so the whole auto-grow
call returned 500 and the FS was never resized. Replace those comments
with ASCII so the script reaches the guest.

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:43:03 +05:00
1a17ceed82 fix(sync): store VM memory/disk in MB/GB, not raw Proxmox bytes
The monitoring worker wrote Proxmox maxmem/maxdisk (bytes) straight into
the memory/disk_size columns, which the rest of the app stores as MB/GB.
Every sync cycle clobbered correct values with bytes, so the saved-config
card showed huge byte numbers under MB/GB labels. Convert via new
_bytes_to_mb/_bytes_to_gb helpers; existing rows self-heal on next sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:42:49 +05:00
074983a1f6 fix(metrics): correct cpu/disk% chart scaling, evict stale collector state, lighten nic query, drop polling & dead RRD code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 21:35:02 +05:00
bbec17abd1 feat(metrics): add collector + retention loops and wiring 2026-06-21 21:35:02 +05:00
6769e5b218 feat(metrics): add /metrics read endpoints for vm and container 2026-06-21 21:35:02 +05:00
e380ec1ab2 feat(metrics): add query_instance_metrics read aggregation 2026-06-21 21:35:02 +05:00
8f9f78ef91 feat(metrics): add blockstats + netstat client wrappers 2026-06-21 21:35:02 +05:00
3d55b1ab8d feat(metrics): add timeframe/bucket math and series aggregation 2026-06-21 21:35:02 +05:00
3a2c87bd0a feat(metrics): add rate + blockstats/netstat/fsinfo parsing helpers
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 21:35:02 +05:00
3b3841fc0e fix(metrics): portable BigInteger PKs + split test asserts 2026-06-21 21:35:02 +05:00