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

96 lines
7.0 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.
# 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).