Commit Graph

377 Commits

Author SHA1 Message Date
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
ebc1c8429e docs: релизы v1.8.1 (Umbrel в App Store) и v1.9.0 (IPAM-привязка, FQDN нод)
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 8s
- VERSION → 1.9.0
- CHANGELOG: запись v1.8.1 (Umbrel как второй источник каталога, адаптер
  compose, иконки из галереи, мультиисточниковый синк, фильтр/бейдж источника,
  кастомный порт установки, мгновенная чистка инстанса) и v1.9.0 (привязка
  IPAM-сетей к серверу/ноде и области с автовыбором в визардах, приём FQDN в
  поле хоста ноды)
- README (en/ru): App Store — мультиисточниковый каталог (Runtipi + Umbrel)
- WIKI: источники, env-настройки Umbrel, иконки из галереи, фильтр/бейдж,
  параметр source в REST

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:32:44 +05:00
markmorado
4c164bec72 chore: убрать план из репозитория и игнорировать docs/plans/
Планы разработки не хранятся в репозитории — только пользовательская
документация. Файл сохранён у автора отдельно.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:28:36 +05:00
markmorado
7e75dc7e28 docs(appstore): дорожная карта — плагин-каркас, золотой шаблон, оптимизация иконок
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
Самодостаточный план для выполнения в отдельной сессии: контекст проекта и
конвенции, текущее состояние (Umbrel-мультиисточник), три workstream'а
(оптимизация каталога/иконок, автосоздание золотого LXC-шаблона, каркас
управляемых плагинов) с пошаговыми инструкциями и критериями приёмки.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:26:17 +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
a6eeae3dd7 feat(ipam): привязка сетей к серверу/ноде и автовыбор сети в визардах
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 8s
- network-scope: scopedNetworks/autoPickNetworkId — фильтрация IPAM-сетей по
  выбранным серверу/ноде и детерминированный автовыбор сети для выдачи IP
- ServerNodeSelect: пара селектов «Сервер + Нода» для привязки сети
- Мастер установки App Store и CreateInstanceWizard: список сетей ограничен
  релевантными, при смене сервера/ноды сеть подбирается автоматически
- IPAM: страницы сетей учитывают привязку к ноде
- Исправлены типы обработчиков Select (onValueChange отдаёт string|null)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 20:09:17 +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
c87a13953e feat(appstore): показывать рабочий порт приложения в каталоге и на карточке
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 8s
Порт (CatalogApp.port) уже был в API и типах, но не выводился в UI. Добавлен вывод в шапку страницы приложения и бейджем в карточке каталога (ключ appstore.port уже в локалях).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:52:08 +05:00
markmorado
e497ae541f fix(appstore): резервный REST-опрос прогресса установки (не зависать при потере WS)
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 8s
Мастер установки получал прогресс только по WebSocket; при потерянном
событии (или долгом docker compose up с тяжёлыми образами) UI замирал на
последнем шаге. Добавлен поллинг /operations раз в 3с как фолбэк.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:50:31 +05:00
markmorado
06c4ec8e94 fix(appstore): заменить нативные confirm() в «Мои приложения» на стилизованный ConfirmDialog
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
Удаление/обновление/откат приложения использовали window.confirm() — не в стиле панели.
Переведено на общий useConfirm (Promise API), удаление — destructive-вариант.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:36:39 +05:00
markmorado
ab5b13a8b8 revert(auth): удалить неиспользуемый AuthPattern
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
Декоративный SVG-паттерн так и не подключён к странице входа —
убираем висячий компонент.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:31:31 +05:00
markmorado
08d4396d2f feat(auth): декоративный SVG-паттерн для страницы входа
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 8s
Компонент AuthPattern — бесшовный геометрический паттерн (SVG <pattern>,
currentColor) для левой панели логина. Пока не подключён к LoginPage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:24:02 +05:00
markmorado
45e998cb3e fix(appstore): убрать реферер и прятать битые картинки в markdown
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
Скриншоты из сторонних README на CDN с hotlink-защитой (cloudinary)
обрывали запрос по Referer (NS_BINDING_ABORTED) и резались браузером
(OpaqueResponseBlocking). Снимаем реферер, мягко скрываем не
загрузившиеся изображения.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:21:46 +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
b2d90d2800 docs(appstore): golden template — брать имя шаблона из pveam available; env-пример
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 56s
Deploy to Production / Trigger production update (push) Successful in 7s
- docs/golden-template.md: шаг 1 разбит на 1a–1d, имя vztmpl берётся из
  `pveam available` в переменную и переиспользуется (фикс no such template /
  volume does not exist)
- backend/.env.example: добавлены APPSTORE_GOLDEN_TEMPLATE и APPSTORE_HOST_ARCH

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:43:25 +05:00
markmorado
799f18d88d fix(appstore): локализация названий категорий (фильтр и бейджи)
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 12s
Категории приложений (gaming, media, network, …) отображаются переведёнными
подписями через i18n (appstore.categories.*, ru+en) при сохранении сырого ключа
как значения фильтра. Неизвестные категории показываются как есть (fallback).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:42:59 +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
490c4ad18d docs(appstore): актуализировать README/CHANGELOG/WIKI, версия 1.8.0
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 12s
- VERSION → 1.8.0
- CHANGELOG (ru/en): запись v1.8.0 с разделом App Store
- README (ru/en): бейдж версии + категория App Store в возможностях
- WIKI: раздел «App Store» (предпосылки, env, install, My Apps, права, REST API) + оглавление

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:12:13 +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
c0296e47b8 Merge pull request 'feat(ui): design polish — semantic status tokens, self-hosted fonts, a11y, skeletons' (#1) from ui/design-polish into main
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 8s
Reviewed-on: #1
2026-07-04 06:42:06 +05:00
markmorado
49e976c0c0 feat(ui): design polish — semantic status tokens, self-hosted fonts, a11y, skeletons
Design system & theme:
- add semantic status tokens (success/warning/danger + track) for both themes
- replace hardcoded green/red/amber/blue across ~20 files with tokens
- fix light-mode bugs (bg-white/10, rgba white gauges) via theme-aware track
- add named text-2xs (11px) step; unify ad-hoc text-[10px]/[11px]
- consistent card elevation (shadow-sm) + opt-in interactive hover

Fonts & perf:
- self-host DM Sans / JetBrains Mono via @fontsource-variable (font-display: swap)
- drop render-blocking Google Fonts link; remove unused Geist dep

Accessibility & mobile:
- global prefers-reduced-motion; focus-visible on custom buttons
- StatusDot role/aria-label; aria-sort on sortable table headers
- Button auto nativeButton=false when rendered as Link/anchor
- 100vh -> dvh in app shell; pointer-coarse min 44px touch targets

Loading & types:
- Skeleton primitive + table loading state on InstancesPage
- fix all 36 pre-existing tsc errors (Select null coercion, SDN query keys,
  Dashboard Rec typing, VMInstance, profile language, JSX namespace)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 06:40:48 +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
e8bce3e8ae fix(sdn): full-width selects in zone/vnet dialogs
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 03:08:41 +05:00
d3fbdb1ff5 fix(sdn): zone DNS is an SDN DNS entry id, not an IP — use select
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 52s
Deploy to Production / Trigger production update (push) Successful in 8s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 03:05:06 +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
87eb573cbd feat(instances): offer SDN vnets as bridges in add-NIC dialog
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 52s
Deploy to Production / Trigger production update (push) Successful in 8s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:57:22 +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
613015cf05 fix(instances): keep table page across live metric updates
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
Live WS metrics replace the table data every second, and TanStack
Table's default autoResetPageIndex snapped the list back to page 1.
Disable the auto reset and go back to page 1 only when search or
filters change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:44:39 +05:00
7e1580579d feat(instances): show bridge comments in add-NIC dropdown
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 9s
Bridge selector now shows the Proxmox interface comment (WAN, Cisco,
subnet, ...) next to the bridge name, like the native Proxmox dialog,
falling back to IPAM/interface CIDR; list sorted by name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:42:44 +05:00
052f9afb65 feat(instances): add/remove network interface from Networking tab
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 1m28s
Deploy to Production / Trigger production update (push) Successful in 8s
- AddNetworkInterfaceDialog: bridge dropdown (node networks), model
  select for VM, VLAN tag, firewall, optional MAC; LXC extras: eth name,
  dhcp/static IP with CIDR + gateway; picks next free netN slot
- NetworkingTab: Add interface button, per-NIC delete with confirmation
  (config.put delete=netN)
- new useNodeNetworks hook; netif i18n keys (en/ru)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:36:20 +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
e366646257 fix(deploy): cacheable build in update watchdog + auto-prune
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 8s
Watchdog rebuilt images with --no-cache on every push, writing fresh
build-cache layers that were never reused. Over ~2 weeks this grew the
Docker build cache to 57GB and filled the production disk.

- Switch `build --no-cache` to `build --pull` (cacheable). Dockerfiles
  already isolate heavy layers (apt/pip/npm) from source COPY, so app/
  frontend changes are still rebuilt — without re-running apt/pip/npm.
- Add `docker image prune` + `docker builder prune until=24h` after a
  successful deploy so old images and stale cache don't accumulate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 20:09:17 +05:00
3622747371 fix(dashboard): make donut tooltip readable
All checks were successful
Deploy to Production / Trigger production update (push) Successful in 7s
Theme tokens are oklch(); wrapping them in hsl(var(--card)) produced an
invalid, transparent tooltip background so the label blended into the
card. Use the oklch vars directly and force readable text colour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 19:48:58 +05:00