Проблема: присоединяемая нода подключалась к кластерной по её адресу из
панели (часто публичный 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>
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>
- Сертификаты: методы клиента + 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>
Ядро:
- 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>
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>
- 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>
Планы разработки не хранятся в репозитории — только пользовательская
документация. Файл сохранён у автора отдельно.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Самодостаточный план для выполнения в отдельной сессии: контекст проекта и
конвенции, текущее состояние (Umbrel-мультиисточник), три workstream'а
(оптимизация каталога/иконок, автосоздание золотого LXC-шаблона, каркас
управляемых плагинов) с пошаговыми инструкциями и критериями приёмки.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Поле 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>
- 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>
- Установка: поле «Хранилище» стало выпадающим списком доступных rootdir-хранилищ
ноды (useLXCStorages) с автовыбором валидного вместо ручного ввода
- Диагностика сбоя: увеличен захват вывода упавшей команды (400 → 2000 символов)
и добавлен 2>&1 к docker compose pull/up — реальная причина (напр. отсутствие
тега образа) больше не обрезается за строками warning
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
Порт (CatalogApp.port) уже был в API и типах, но не выводился в UI. Добавлен вывод в шапку страницы приложения и бейджем в карточке каталога (ключ appstore.port уже в локалях).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Мастер установки получал прогресс только по WebSocket; при потерянном
событии (или долгом docker compose up с тяжёлыми образами) UI замирал на
последнем шаге. Добавлен поллинг /operations раз в 3с как фолбэк.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Удаление/обновление/откат приложения использовали window.confirm() — не в стиле панели.
Переведено на общий useConfirm (Promise API), удаление — destructive-вариант.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Компонент AuthPattern — бесшовный геометрический паттерн (SVG <pattern>,
currentColor) для левой панели логина. Пока не подключён к LoginPage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Скриншоты из сторонних README на CDN с hotlink-защитой (cloudinary)
обрывали запрос по Referer (NS_BINDING_ABORTED) и резались браузером
(OpaqueResponseBlocking). Снимаем реферер, мягко скрываем не
загрузившиеся изображения.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
При удалении 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>
- 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>
Категории приложений (gaming, media, network, …) отображаются переведёнными
подписями через i18n (appstore.categories.*, ru+en) при сохранении сырого ключа
как значения фильтра. Неизвестные категории показываются как есть (fallback).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Описания приложений: безопасный рендер 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>
- 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>
Добавлена возможность пробросить физический диск ноды в VM по стабильному
пути /dev/disk/by-id/ (scsiN/virtioN/sataN) в карточке «Проброс устройств».
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Карточки на странице «Узлы» дополнены важной информацией:
- версия Proxmox VE (чип «PVE x.y.z»);
- число нод в кластере (для кластерных серверов);
- аптайм узла;
- чипы рабочих областей, в которых состоит сервер.
Бэкенд: эндпоинт /api/{id}/nodes отдаёт version; ответ серверов
дополнен полями cluster_name и workspaces (ServerWorkspaceBrief).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Реальные интервалы циклов: dashboard 2с, server 2с, instance 1с.
В комментариях было устаревшее 10с/5с. Логику не меняем — дашборд
уже обновляется раз в 2 секунды.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Данные по узлам (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>
- поднять лимит /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>
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>
- 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
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>
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>
- 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>
- 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>
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>
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>