Files
preland/admin
Panfilov Konstantin d04b40519e
deploy-preland / deploy zoomstage.online (push) Successful in 58s
fix(admin): persist private sessions across container replacement
2026-10-25 18:26:41 +03:00
..

Админка прелендингов — preland#22

Отдельное PHP-приложение для admin.zoomstage.online. Формы редактируют фактические config.json, выбранные поля применяются к одному или нескольким доменам. ZIP indpic проверяется по контракту CPanel и доставляется на выбранные хостинги. Изменение контента не требует Git/push/deploy; admin/ не входит в публичную поставку прелендингов.

Спецификация и план — docs/superpowers/{specs,plans}/2026-10-25-preland22-* в репозитории, копии и фактический статус — в базе знаний, основной реестр — preland#22.

Сводка и конструктор ссылки

Первый экран читает сохранённые конфиги всех подключений: основной сайт, реквизиты, дополнительные поля, default и все веса UTM. У каждой колонки своё время чтения, версия, обновление и ошибка; при отказе прежний снимок явно помечается. Обновление сводки не сбрасывает черновик редактора и не меняет его версию для CAS.

Конструктор работает отдельно: значения или «Рандом» в восьми списках, затем «Сформировать и сохранить». Последний выбор и точный URL сохраняются на сервере отдельно по ID домена, восстанавливаются после перезагрузки. Изменение списка скрывает старую ссылку до явного сохранения. Значащая цифра стоит на пятой позиции метки, 0 означает выбор по весам. Конструктор не меняет config, default, веса или наборы, не создаёт задания; сохранённый выбор доступен и при недоступном хостинге.

GET /api/link-builder?target=ID читает выбор, POST /api/link-builder сохраняет {target,selection} с ровно восемью каноническими UTM-ключами. Действуют общий вход и CSRF. Состояние находится в приватном state/link-builder-ID.json с режимом0600. При переименовании домена меняется origin ссылки, выбор и сохранённые метки остаются. Неизвестная в текущем runtime/catalog группа сопровождается предупреждением; конструктор не исправляет конфигурацию автоматически.

Состав и доступы

compose.yaml запускает web и отдельный worker из одного неизменяемого образа. Нужны PHP8.3, ZIP, Imagick с AVIF/WebP/JPEG и phpseclib3 из зафиксированного Composer lock. На Windows ничего устанавливать не требуется: сборка и проверки выполняются Docker.

На сервере /srv/preland-admin: compose.yaml, .env с ADMIN_IMAGE, закрытый secret.env, state/, secrets/ и deploy/. В secret.env задаются ADMIN_USERNAME и ADMIN_PASSWORD_HASH; bcrypt-значение заключается в одинарные кавычки, чтобы Compose не интерполировал $. Пароль хешируется через bin/password-hash.php со stdin. В HTTP Secure-cookie включена по умолчанию; ADMIN_COOKIE_SECURE=0 разрешена только на изолированном HTTP-стенде.

PHP-сессии хранятся в state/sessions с режимом0700, файлы имеют режим0600. Пересоздание web-контейнера сохраняет вход; срок бездействия остаётся3600секунд. Хранилище сессий не допускает относительный путь, symlink или публичный каталог. При первом переходе со старого ephemeral backing существующие session-файлы нужно перенести приватно до пересоздания; сам код не мигрирует старый /tmp.

UID/GID приложения — 997:987. Приложение получает приватный state и ровно два local mount: /targets/zoompixel.ru, /targets/zoomstage.online. Весь /srv/preland, Docker socket, root SSH key и база основного сайта в приложение не передаются. Права публикации на каждом корне проверяются реальным временным файлом/rename при проверке подключения; нельзя включать запись до успешной проверки.

Два REG.RU-хостинга подключаются SFTP/SSH с их обычными ограниченными учётными записями. Нужны абсолютный корень, host key либо SHA256 fingerprint, пароль или приватный ключ, доступный через stdin CLI bin/target-import.php. Секрет хранится 0600 в приватном state, по API не возвращается. На удалённом хосте нужен php CLI >=8.1 с fsync; удалённый Imagick не нужен. Замена реквизитов сбрасывает проверку подключения.

Работа через формы

  1. Выбрать исходный домен: данные и SHA-версия читаются с его хостинга.
  2. Изменить поля. Отмечаются только введённые поля; можно явно выбрать группу/всю форму.
  3. Отметить домены и нажать «Применить к выбранным». Каждый домен использует собственную прочитанную версию и сохраняет свои остальные значения. После сохранения перечитать домен.
  4. В журнале проверить отдельные статусы. Успешные цели не повторяются. Конфликт требует перечитать данные; после неизвестного результата повтор сначала сверяет фактический хеш.
  5. В indpic выбрать ZIP либо проверить уже загруженную SFTP-папку. Новый набор сам не меняет default/веса. Чтобы назначить новый номер indpic default, он должен присутствовать в таблице весов — можно с весом0. Это действующее правило resolver, а не вес трафика.

Номера — 1–9, ZIP содержит папку номера и ровно90 изображений установленных форматов/ размеров; лимит8МиБ на файл,64МиБ распакованного набора. При нулевой сумме весов rnd использует первый ключ weights по порядку config.json; только cons использует default. Имена numeric/legacy UTM адаптируются отдельно для каждой цели, конфликтующие aliases блокируются. Удаление набора из реестра оставляет файлы; используемый номер удалить нельзя.

Каталог групп и приватная публикация

deploy/export-catalog.php выполняется внутри существующего API-контейнера минимальным Yii bootstrap, в read-only DB-транзакции. Он экспортирует только номера/названия degr/tp/cons, не выдавая приложению доступ к БД. deploy/refresh-catalog.sh запускается host cron раз в минуту от root. Подготовка/проверка/chown происходят в root-only secrets/, затем mv -fT заменяет фиксированный state/catalog.json без следования ссылке назначения. Снимок действует180секунд только для совпадающего основного host. Недоступный/старый каталог даёт явную пометку непроверенного существования групп, а не выдуманный список.

Резервы и journal локального транспорта хранятся в приватном state. Для exact bind mount папка подготовки .preland-admin-staging создаётся внутри корня с0700; публичный веб-сервер другого UID обязан получать403 и через внешний origin, и через прямой loopback Apache. При наличии nginx-правила deny оно служит дополнительной защитой. Одной надежды на .htaccess недостаточно: AllowOverride может быть выключен. Каталог активного /N/ —0755, файлы0644. SFTP использует приватную sibling-папку вне webroot.

Переключение каталогов имеет два rename и durable journal. При аварии upload-child supervisor сразу пытается восстановить полный прежний/новый набор под тем же lock; при полном перезапуске это делает следующая операция транспорта. Неподтверждённый результат остаётся unknown. Это восстановление после прерывания, а не обещание отсутствия краткого окна между двумя rename. При недоступном SSH восстановление нельзя подтвердить.

Сборка, проверки и выпуск

docker build -t preland-admin:<git-sha> -f admin/Dockerfile admin
docker run --rm --entrypoint composer -v "$PWD:/work:ro" -w /work/admin preland-admin:<git-sha> test
docker run --rm --entrypoint bash -v "$PWD:/work:ro" preland-admin:<git-sha> -c 'sed "s/\r$//" /work/admin/tests/catalog-publish.sh | bash'

Дополнительные Docker проверки: tests/backend-sftp.Dockerfile — настоящий SSH/SFTP, tests/backend-worker.php — цикл с паузой65секунд, tests/ui-*.cjs — реальные browser сценарии. Последние выполняются только на disposable целях; они изменяют и восстанавливают настройки. NODE_PATH=/e2e/node_modules, CHROMIUM_PATH задаёт браузер имеющегося e2e-образа.

Для production подготовить отдельный TLS/nginx по deploy/nginx.conf, импортировать проверенные подключения, сохранить резерв состояния, указать точный image в .env, затем docker compose up -d. Проверить web/worker health, вход, четыре чтения, безопасный реальный цикл сохранения/перечитывания, ZIP и публичные изображения. Не заменять runtime-конфиги Git-снимком. Для отката приложения вернуть прежний ADMIN_IMAGE; state и контент сохраняются. Возврат контента выполняется отдельно через резерв и CAS, не через старый Docker-образ.

Фактические production-команды, версии, ограничения, свидетельства UI и статус подключения четырёх доменов фиксируются в preland#22 и KB; этот README сам не доказывает завершение выпуска.