175 lines
19 KiB
Markdown
175 lines
19 KiB
Markdown
# Единая админка прелендингов — 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-вход не доказывает готовность центральной админки.
|