diff --git a/docs/superpowers/specs/2026-10-25-preland22-admin-design.md b/docs/superpowers/specs/2026-10-25-preland22-admin-design.md new file mode 100644 index 0000000..9ddc25f --- /dev/null +++ b/docs/superpowers/specs/2026-10-25-preland22-admin-design.md @@ -0,0 +1,174 @@ +# Единая админка прелендингов — preland#22 + +Дата: 25.10.2026. Статус: проект на согласование; реализация и выпуск ещё не выполнены. +Реестр: http://git.dira89kv.beget.tech:3000/anton/preland/issues/22. +Размещение и доступы: preland#21. Секреты в документ не входят. + +## Задача владельца + +Одна защищённая админка на поддомене stage управляет фактическими config.json и +наборами indpic четырёх независимых прелендингов. Редактирование и публикация контента +проходят без Git, ручной работы с JSON и разноса файлов через FTP-клиент. +Домены и хостинги можно добавлять, менять и отключать через эту же админку. + +Начальные цели: zoomila.ru, zoomika.ru, zoompixel.ru, zoomstage.online. +Отключённые .online не подключаются. Рабочее имя админки: admin.zoomstage.online. +Размещение на отдельном поддомене и повторное использование редактора indpic CPanel +поручены владельцем; конкретное имя admin и технические решения ниже предложены агентом. + +## Подход + +Предлагается отдельное PHP-приложение в репозитории preland, с собственным Docker-образом +и отдельной поставкой. Оно использует SFTP/SSH, а для каталогов на том же сервере — +ограниченный файловый адаптер. Работа с конфигом не требует запуска деплоя. +Управление не публикуется внутри поставки обычных прелендингов. + +Перенос всей Yii2 CPanel потребовал бы базы и зависимостей основного сайта; его API +и релиз стали бы зависимостью обычного изменения настроек. Поэтому переносится +конкретный редактор наборов, его внешний вид, поведение и файловый валидатор, +а зависимые от БД операции заменяются файловым реестром админки. +Клиентский FTP из браузера не подходит для серверных проверок и хранения доступов. + +Источник переиспользования: api origin/master 591a67cc8302, +components/indpic/IndpicSetStorage.php и indpic-блок +modules/cpanel/views/settings/index.php, соответствующие действия SettingsController. +Копия снабжается ссылкой на источник и совместимыми контрактными тестами. +Бизнес-логика основного API, платежи, PIN и маршруты основного сайта не меняются. + +## Экран настроек + +Список доменов показывает stage/production, состояние соединения и последнее чтение. +Открытие домена читает текущий удалённый файл; локальная копия не считается источником +истины. При недоступности хоста сохранение в него недоступно, другие цели работают. + +Вместо текстового JSON — поля адреса основного сайта, строки реквизитов и восемь +групп UTM: indpic, theme, degr, reg, banch, tp, cons, focus. Для каждой группы — +вариант по умолчанию, таблица вариантов и неотрицательные веса, пояснения значений. +Поддерживаются добавление и удаление строк весов. Реквизиты редактируются строками. +Дополнительные уже существующие поля показываются типизированными вложенными полями +(текст, число, переключатель, массив, объект), без редактора JSON. Отсутствующие +неизвестные поля не создаются автоматически. Секреты не выводятся в диагностику. + +Текущий zoompixel.ru может содержать старые semantic-ключи/значения. Они отображаются +понятными вариантами через действующий utm-codec; сохранение меняет только выбранные +поля. Неизменённые поля, их типы и значения сохраняются. Конфликтующие старый и новый +ключи нельзя молча объединять: показывается понятная ошибка до записи. + +После редактирования пользователь отмечает один или несколько доменов и нажимает +«Применить к выбранным». Перед кнопкой видны выбранные поля и домены. По умолчанию +отмечен только открытый домен; нет скрытого автоматического выбора всех целей. +Распространяются только изменённые/явно выбранные поля, остальные настройки каждой +цели сохраняются. Можно явно выбрать целую группу UTM или все поля формы. +Если значения между доменами отличаются, показывается «Разные значения»; без явного +ввода пользователя это состояние не превращается в новое значение. + +До публикации для каждой цели проверяются типы/структура, допустимые UTM 1–9, +варианты theme/reg/banch/focus, веса, существование indpic для default и ненулевых +весов. Для degr/tp/cons используются номера групп, проверяемые по доступным данным +основного сайта. Если каталог доступен, неизвестный номер блокируется. Если каталог +недоступен, разрешён ручной выбор номера 1–9 с явной пометкой «Существование группы +на основном сайте не проверено» в форме и результате; сохранение не выдаётся за +проверку API. Это решение исполнителя сохраняет независимость редактора от API; +соответствующие состояния включены в приёмку. Выдуманного списка групп нет. +Сумма весов может быть нулевой: действующий +преленд тогда использует default. Адрес сайта — допустимый HTTPS-хост без пути. + +## Сохранение и массовые операции + +Каждая форма хранит версию прочитанного файла (SHA-256). Непосредственно перед записью +проверяется текущая версия под блокировкой публикации. Если файл изменился снаружи, +цель получает конфликт и предложение перечитать/повторно применить изменения. +Чужая версия не затирается. Успешная запись: резерв вне webroot, временный файл, +проверка JSON, атомарная замена и повторное чтение/сверка. Протокол и адаптер должны +поддерживать этот контракт, иначе соединение нельзя включить для записи. +SSH доступен на начальных REG.RU-хостах и позволяет выполнить проверку/замену под +удалённой блокировкой; одной локальной блокировки админки недостаточно. + +Массовое применение — независимые операции с отдельными статусами: ожидание, +выполняется, сохранено и проверено, конфликт, ошибка, неизвестный результат. +Сбой одной цели не отменяет уже успешные и не мешает остальным. Общего обещания +«всё сохранено» при частичном успехе нет. Повтор выполняется только для выбранных +неуспешных целей; после таймаута сначала перечитывается фактическое состояние. +Задание и результаты сохраняются на сервере, обновление страницы их не теряет. +Есть журнал с доменом, временем, изменёнными полями и исходом без паролей. +Восстановление выбранной резервной версии проходит ту же защиту от конфликта. + +## Наборы indpic + +Переиспользуется таблица CPanel: номер/название, default, проверка, ZIP, действия; +добавление ZIP либо проверка набора, уже загруженного по SFTP, модальное окно результата. +Номер для прелендинга ограничен 1–9 действующим внешним UTM-контрактом. +Название хранится в приватном реестре админки, default/веса — в config.json цели. + +Один архив можно отправить на один или несколько выбранных доменов. Проверка до +доставки: каталог номера набора и ровно 90 обязательных изображений, отсутствие +лишних/повторных/вложенных/опасных путей, чтение каждого изображения и размеры +из существующего IndpicSetStorage. Лимиты ZIP, распаковки и времени задаются сервером. +Проверенный набор передаётся в непубличную подготовительную папку на целевом хосте, +проверяются все файлы/SHA-256, затем переключается каталог с резервом и откатом при +ошибке. Папки подготовки/резерва никогда не обслуживаются веб-сервером. +Для уже загруженного по SFTP набора файлы проверяются тем же валидатором. + +Загрузка не меняет default и веса автоматически. Назначение default — явная операция +сохранения config.json. Нельзя активировать неполный набор. Замена существующего +набора предварительно предупреждает о текущем использовании. Удаление из реестра, +как в CPanel, оставляет файлы; нельзя убрать набор, используемый default или весом>0. +Удаление физического контента не входит в исходное поручение. + +## Подключения и защита + +Отдельный вход по логину/паролю, хеш пароля, HTTPS, HttpOnly/SameSite session, +CSRF, ограничение попыток. Админка использует отдельные доступы ограниченного объёма; +приватный ключ root основного сервера приложению не передаётся. +Список целей, их имена, окружение, хост/порт, пользователь, корневая папка, +проверенный ключ SFTP и секрет редактируются на странице подключений. +Секрет можно заменить, но нельзя прочитать обратно в интерфейсе. +Проверка соединения должна подтвердить права и реальные пути до включения записи. + +Приватное состояние, ключи, резервные копии, архивы и журнал — вне webroot/Git, +с ограниченными правами и персистентными Docker-томами. Удаление подключения отключает +управление и убирает его из списка целей, не удаляя сайт и контент хостинга. +Одновременные операции на одном конфиге/наборе сериализуются. Приложение не выполняет +произвольные команды, пути ограничены каталогом выбранной цели, host key проверяется. + +## Независимость контента от Git и выпуск + +Текущий workflow не заливает /N/, но --stage-sets сравнивает их с Git, а --remote +также включает их в общее сравнение. После замены существующего набора это блокировало +бы последующий выпуск кода. В #22 разделить проверки: поставляемый код/статические +ресурсы проверяются по manifest Git, runtime-наборы — по фактическому контракту 90 +изображений и действующим ссылкам config, без требования импорта в Git. +Проверки не выключаются; тестами доказывается, что корректная замена набора пропускается, +повреждённый активный набор блокирует проверку, кодовой deploy не меняет config и /N/. +Устаревшие требования обязательной синхронизации наборов в Git обновляются явно по #22. + +Админка выпускается отдельно от прелендов, с TLS на admin.zoomstage.online и четырьмя +подключениями. Изменение UTM пользователем не вызывает commit/push/CI. Реальные +production-настройки/наборы при установке не меняются. Проверочные записи — в выделенной +тестовой цели/наборе с последующим контролируемым восстановлением; отсутствие доступа +или непроверенный сценарий оставляет #22 открытой. + +## Приёмка + +В Docker: контракт валидатора CPanel, JSON-типы/semantic-совместимость, патч только +выбранных полей, конфликт и два одновременных сохранения, резерв/откат, разрыв связи, +таймаут после записи, повтор задания. Локальная SFTP-цель проверяет настоящий протокол. +ZIP: корректный набор, отсутствующий файл, размер/формат, лишний/повторный путь, +traversal, лимиты, ошибка публикации и сохранение старого рабочего набора. + +В штатном браузере: вход, открыть/изменить/сохранить/перезагрузить конфиг; разные +значения, выбор одного/двух доменов и только нужных полей; частичный отказ, повтор, +конфликт и восстановление; ZIP и ошибка с последующей успешной загрузкой; управление +подключениями. Desktop/mobile и матрица всех общих состояний/четырёх целей. +На установленной админке: TLS/авторизация, чтение всех четырёх целей, безопасный +реальный цикл записи/повторного чтения и загрузки на тестовых данных. Итог, версия, +проверки и ограничения записываются в #22 и KB до закрытия задачи. + +## Решения исполнителя и незавершённое + +На усмотрение исполнителя: PHP-библиотека SFTP, детали приватного файлового реестра, +лимиты и хранение заданий, распараллеливание в пределах ресурсов. Выбор подтверждается +тестами и совместимостью с существующим хостингом; установки на Windows-хост нет. +Пока не выполнены: согласование этого проекта, реализация, проверки, настройка DNS/TLS, +выдача ограниченных доступов и запуск. Секреты/возможности среды требуют фактической +проверки; исторический успешный SFTP-вход не доказывает готовность центральной админки.