Files
pvemanager/docs/appstore-engine.md
markmorado 0606e8ac77
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 1m4s
Deploy to Production / Trigger production update (push) Successful in 12s
feat(appstore): модуль «App Store» — каталог self-hosted приложений (1 app = 1 LXC + Docker Compose)
Полный цикл 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

7.0 KiB
Raw Blame History

App Store — M2 App Engine (установка)

Установка приложения из каталога: клон золотого шаблона → pct push compose/.env → docker compose up -d → health-check, с журналом шагов, статусами и WS-прогрессом. Ядро переиспользует пайплайн M0 (app/appstore/pipeline.py).

Компоненты

  • Модели: InstalledApp, AppOperation (backend/app/models/appstore.py).
  • Миграция: migrate_appstore_installed() (таблицы installed_apps, app_operations).
  • Движок: backend/app/services/appstore_engine.py — install, retry_install, delete_app, build_env (платформенные переменные ТЗ 5.3 + форма + генерация random-секретов).
  • API: backend/app/api/appstore/apps.py.
  • Прогресс: WebSocket /ws/tasks, события appstore_operation_update / appstore_app_deleted.

Обязательная настройка

APPSTORE_GOLDEN_TEMPLATE — volid золотого vztmpl с предустановленным Docker, напр. local:vztmpl/golden-docker_12_amd64.tar.zst (готовится вручную, M0). Без него установка отклоняется с понятной ошибкой.

API

POST   /api/appstore/apps/install          (app:install)  → {installed_app_id, operation_id, vmid, secrets}
GET    /api/appstore/apps                   (app:view)
GET    /api/appstore/apps/{id}              (app:view)     → + catalog, update_available
GET    /api/appstore/apps/{id}/operations   (app:view)     → журнал шагов
GET    /api/appstore/apps/{id}/credentials  (app:view)     → секреты формы (показать один раз)
POST   /api/appstore/apps/{id}/retry        (app:manage)   → повтор без пересоздания LXC
DELETE /api/appstore/apps/{id}              (app:manage)   → stop + destroy LXC + удалить запись

install тело запроса: app_id, name, server_id, node?, form_answers{}, cores, memory, disk, storage, bridge, ostemplate?.

Поведение (ТЗ 6.1)

  • Пайплайн асинхронный (ThreadPoolExecutor), статус и журнал шагов пишутся в БД, прогресс транслируется по WS.
  • secrets (пароли/сгенерированные random) возвращаются один раз в ответе install; в БД хранятся зашифрованно (Fernet, env_encrypted).
  • Сбой любого шага → installed_apps.status='error', app_operations.status='failed' с текстом ошибки; доступны «Повторить» (/retry) и «Удалить» (DELETE).
  • Идемпотентность повтора: vmid сохраняется в записи; при повторе контейнер не пересоздаётся (проверка существования по vmid), пайплайн продолжает с доставки/запуска.
  • pct exec/pct push — через SSH на ноду; значения .env очищаются от переводов строк, файлы доставляются base64 → pct push (без shell-инъекций, S-4).

Verification (DoD M2 — backend)

Предпосылки: настроен APPSTORE_GOLDEN_TEMPLATE, каталог синхронизирован (M1), SSH-доступ панели к ноде.

  1. POST /api/appstore/apps/install {app_id:"uptime-kuma", name:"uk1", server_id:1} → 202-подобный ответ с operation_id; по WS приходят шаги; статус → running.
  2. GET /api/appstore/apps/{id} → status:running, url:http://IP:port.
  3. Открыть url — приложение отвечает.
  4. Приложение с формой (напр. wordpress): random-пароли БД генерируются, GET /apps/{id}/credentials их возвращает.
  5. Сбой (напр. неверный шаблон) → status:error, журнал с текстом; POST /retry не создаёт второй LXC; DELETE сносит контейнер и запись.

UI (сетка, мастер установки, My Apps) — под-этап M2 (готов).

M3 — Lifecycle + реконсиляция

Добавлено в appstore_engine.py / api/appstore/apps.py:

POST /api/appstore/apps/{id}/action/{start|stop|restart}   (app:manage)
GET  /api/appstore/apps/{id}/logs?tail=200                 (app:view)  — docker compose logs через pct exec
POST /api/appstore/apps/reconcile                          (app:view)  — сверка LXC↔БД
  • lifecycle_action — start/stop/restart LXC через ProxmoxClient, обновление статуса + WS.
  • get_logs — pct exec … docker compose logs --tail N по SSH (ТЗ 6.5, по запросу).
  • reconcile (ТЗ 6.6) — фон каждые 60с (start_reconcile_scheduler) + при открытии «My Apps»: пропавший контейнер → orphaned; статус синхронизируется с Proxmox (running/stopped).
  • UI: кнопки Start/Stop/Restart/Logs/Delete в My Apps, диалог логов, бейдж orphaned.

DoD M3: все операции работают, orphan-детект работает.

M4 — Update + Rollback (снапшоты Proxmox)

POST /api/appstore/apps/{id}/update    (app:manage)  body: {form_answers?}
POST /api/appstore/apps/{id}/rollback  (app:manage)
  • update (ТЗ 6.3): снапшот preupd_<version>_<timestamp> → pct push свежего compose из каталога (poc_update) → docker compose pull && up -d → health-check. Успех → новая версия в БД, хранится последний 1 pre-update снапшот (старые удаляются). Провал health → статус update_failed, доступен откат. .env сохраняется, новые поля формы дозапрашиваются (form_answers). Версия/tipi снапшота фиксируются в installed_apps.snapshot_version/snapshot_tipi_version (миграция добавляет колонки).
  • rollback (ТЗ 6.4): stop → rollback_container_snapshot(start=1) → start → health-check → возврат прежней версии в БД. UI обязательно предупреждает, что откат возвращает и ДАННЫЕ приложения на момент снапшота.
  • UI: кнопки Update (если update_available) и Rollback (если есть last_snapshot) в My Apps, с подтверждением-предупреждением.

DoD M4: обновление и откат Nextcloud проходят без потери данных (данные — в томах контейнера, снапшот покрывает весь CT).