Полный цикл 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>
7.0 KiB
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-доступ панели к ноде.
POST /api/appstore/apps/install {app_id:"uptime-kuma", name:"uk1", server_id:1}→202-подобный ответ сoperation_id; по WS приходят шаги; статус →running.GET /api/appstore/apps/{id}→status:running,url:http://IP:port.- Открыть
url— приложение отвечает. - Приложение с формой (напр.
wordpress): random-пароли БД генерируются,GET /apps/{id}/credentialsих возвращает. - Сбой (напр. неверный шаблон) →
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).