Skip to content

ТЗ — Панель разработчика (cms/dev-panel)

Слой: 🔵 инфра-модуль · Зрелость доноров: ★ · Донор: — Статус: ТЗ к разработке

Назначение и возможности

Диагностические Filament-страницы для studio-роли: инспектор кеша, карта модулей и хуков, просмотр очередей и failed jobs, запуск только безопасных artisan-команд. Деструктивные операции (в т.ч. migrate:fresh и любой снос данных) в панель принципиально не включаются — только safe-подмножество.

  • Инспектор кеша: просмотр групп настроек, тегов page-cache, ручной сброс тега;
  • карта модулей и хуков (визуализация cms:map/cms:hooks в UI);
  • просмотр очередей Horizon и failed jobs с возможностью retry/delete конкретной задачи;
  • запуск ограниченного whitelist безопасных команд (cache:clear, view:clear, config:clear, queue:restart) — без произвольного ввода команды;
  • просмотр текущих feature-flags и статуса модулей (installed/enabled/disabled);
  • доступ строго под studio-ролью, не выдаётся обычным администраторам клиента;
  • тройной независимый гейт доступа (env + настройка-kill-switch + роль) на уровне middleware, до рендера страницы и до выполнения любого action — подробности и таблица слоёв в «Безопасность»;
  • удалённый kill-switch dev-panel.remote_disabled: студия форсирует принудительное выключение панели на managed-парке дистанционно, клиент не может включить её обратно самостоятельно;
  • массовый retry failed jobs одним действием, с подтверждением при большом количестве.

Зависимости и выключение

requires: — · provides: dev-panel · выключение не влияет на работу сайта — это чисто диагностический инструмент без собственных данных в рантайме.

Поведение при выключении: панель пропадает из меню админки, все остальные модули работают без изменений — деградации функциональности сайта нет.

Модель данных

Своих таблиц нет — использует существующие данные ядра (settings-store, реестр модулей, Horizon/Redis) только на чтение и точечные безопасные операции. Лог вывода запущенных команд хранится в файловом логе модуля (не в БД), ретеншн — dev-panel.command_log_retention_days, без отдельной таблицы и миграций.

ПДн-паспорт: модуль ПДн не хранит намеренно — своих таблиц нет, диагностические данные (кеш-теги, реестр модулей, метаданные failed jobs) не являются персональными. Риск утечки — косвенный: failed job в очереди может нести сериализованный payload джобы, которая сама работала с ПДн (например, заказ или профиль пользователя). Панель и лог вывода команд обязаны показывать/хранить по failed job только метаданные — id, класс джобы, queue, attempts, статус, время постановки/провала — не полный payload/аргументы задачи; просмотр полного payload — задача существующего инструмента диагностики очереди (Horizon UI под той же studio-ролью), не панели.

Входные и выходные данные

Входы

ИсточникДанные/поляЧем валидируется
Filament action: запуск командыcommand (одна из whitelist)сверка со списком dev-panel.allowed_commands, любое другое значение → отказ на уровне сервиса, не только UI
Filament action: сброс тега кешаtag (строка тега)permission dev-panel.manage; тег — существующий в CacheTags-реестре, произвольная строка не принимается
Filament action: retry/delete failed jobjob_idpermission dev-panel.manage; job_id — существующая запись в Horizon failed jobs
Тройной гейт доступа (при каждом запросе к панели и перед выполнением любого action)APP_ENV, dev-panel.remote_disabled, dev-panel.enabled_for_studio_only, роль пользователяmiddleware проверяет все три слоя независимо (env / настройка-kill-switch / роль); отказ любого — 403 до рендера страницы; таблица слоёв — «Безопасность»

Выходы

ПотребительДанныеФормат
Filament UIлог вывода команды, список failed jobs, карта модулейBlade через сервис модуля
Horizon/Redisretry/delete конкретной задачипрямой вызов API Horizon (только safe-операции)
CacheTags ядрасброс конкретного тегавызов контракта ядра, не raw Cache::flush()

Настройки (группа dev-panel)

КлючТипДефолтaffectsPageCacheОписание
dev-panel.allowed_commandsarray["cache:clear","view:clear","config:clear","queue:restart"]Whitelist команд, доступных из UI
dev-panel.failed_jobs_page_sizeint25Размер страницы списка failed jobs
dev-panel.enabled_for_studio_onlybooltrueОдин из трёх независимых слоёв гейта (см. «Безопасность») — сама по себе не гарантирует ограничение доступа, роль и env проверяются отдельно
dev-panel.remote_disabledboolfalseKill-switch managed-парка: студия форсирует true дистанционно через флаги парка; клиент не может выставить false сам, пока студия не снимет форс — третий слой гейта
dev-panel.command_log_retention_daysint7Хранение лога вывода запущенных команд
dev-panel.max_bulk_retryint50Порог количества задач, выше которого массовый retry требует подтверждения

Лимиты (max_bulk_retry, failed_jobs_page_size) и ретеншн (command_log_retention_days) — явные настройки с дефолтами по критерию «Лимиты и квоты» матрицы v2.2: достижение max_bulk_retry не выполняет массовую операцию молча, а требует подтверждения («Крайние случаи»); failed_jobs_page_size держит список failed jobs постраничным вместо неограниченной выборки; ретеншн лога команд — обязательный, не бессрочное накопление на диске клиента.

API

Отдельного публичного API нет — админ-CRUD и вызовы команд через Filament actions, защищённые той же авторизацией, что и остальная админка (studio-роль).

Компоненты

Filament: страница «Кеш» (инспектор групп/тегов), страница «Карта модулей» (граф зависимостей requires/suggests/provides, поиск/фильтр при большом числе модулей), страница «Очереди» (failed jobs с пагинацией failed_jobs_page_size, retry/delete поштучно и массово), страница «Команды» (кнопки whitelist-команд с логом вывода, хранящимся command_log_retention_days). Команды: cms:dev-panel:doctor --json (подтверждает, что деструктивные команды не входят в whitelist ни при какой конфигурации, и что все три слоя гейта — env, remote_disabled/ enabled_for_studio_only, роль — сконфигурированы и активны одновременно, не только один из них).

События и обмен

СобытиеКогдаPayload
DevPanelCommandExecutedЗапуск whitelist-команды из UIcommand, user_id, result
DevPanelCacheTagFlushedРучной сброс тега кеша из инспектораtag, user_id

result в DevPanelCommandExecuted — статус завершения и код возврата команды, не сырой вывод целиком (сырой вывод — только в файловом логе модуля со своим ретеншном, не в событии, чтобы не тиражировать потенциально чувствительный текст по слушателям события).

Provides-контрактов не реализует и не потребляет. Читает реестр модулей и их requires/suggests/provides через контракты ядра (не сырыми запросами) для карты зависимостей. Слушает только собственные UI-действия (запуск команды, сброс тега).

Таблица взаимодействий

Сущность/модульКаналНаправлениеЧто происходит
Реестр модулей ядрачтение через cms/core-contractsinПостроение карты зависимостей requires/suggests/provides
CacheTags ядрасервис-вызов контракта ядраoutРучной сброс конкретного тега по действию администратора
Horizon/Redis (очереди)внутренний сервис-вызов (не модуль CMS)in/outПросмотр failed jobs, retry/delete конкретной задачи
Все остальные модули (косвенно)безучастны — панель только читает их публичные данные диагностикиinМодуль не инициирует обмен ни с одним модулем напрямую

Фоновая работа

Фоновой работы нет — все действия (запуск whitelist-команды, retry/delete failed job, сброс тега кеша) выполняются синхронно по клику администратора.

Метрики и ранбук

Панель — диагностический инструмент и сама не алертит (алерты — зона cms:health/ cms:sentry), но визуализирует здоровье очередей и историю собственных действий:

  • метрики: количество failed jobs в очереди (источник — Horizon), количество выполненных whitelist-команд за период (источник — файловый лог модуля);
  • симптом → что проверить → команда:
    • панель доступна пользователю без studio-роли на проде → немедленный аудит (кто получил доступ и когда, не форсирован ли remote_disabled=false ошибочно) → форс dev-panel.remote_disabled=true студией + ревизия ролей → cms:dev-panel:doctor --json;
    • массовый retry failed jobs не проходит или зависает → проверить доступность очереди/Horizon (воркеры живы, Redis отвечает) → php artisan horizon:status;
    • карта модулей в UI не строится или расходится с ожидаемой → сверка независимо от панели → cms:map --json;
    • список failed jobs непрерывно растёт → первопричина не в панели, а в конкретной джобе-источнике → смотреть логи и health-чек модуля-владельца джобы, не диагностику панели;
    • лог вывода whitelist-команд не пишется → проверить права на файловый лог модуля и что command_log_retention_days не 0cms:dev-panel:doctor --json.

Производительность и кеш

Объёмы: количество failed jobs — от нуля до сотен на нездоровой очереди (для этого и существует список с пагинацией failed_jobs_page_size); карта модулей — десятки-сотни модулей на инсталляцию, строится одним проходом по реестру, не по одному запросу на модуль (N+1 по requires/suggests/provides недопустим). Горячего пути в публичном смысле нет — весь функционал admin-only и не участвует в бюджете запросов сайта. Собственных тегов не объявляет — инспектирует и сбрасывает чужие теги по явному действию администратора (не автоматически); на page-cache влияет только через такой ручной сброс, и то лишь для затронутого тега, не глобально. Пагинация failed_jobs_page_size и порог max_bulk_retry — не только UX-ограничение, но и защита очереди от нагрузки при нездоровом состоянии с сотнями failed jobs.

Безопасность

Filament actions — единственная точка входа, whitelist команд жёстко зафиксирован в конфиге, не расширяется произвольным вводом. Деструктивные команды (migrate:fresh, db:wipe и подобные) не входят в whitelist ни при какой конфигурации — гарантия на уровне кода, не только документации. Лог вывода команды в UI не должен утекать в общий лог с секретами (например, вывод queue:restart не содержит переменных окружения); ПДн-паспорт — см. «Модель данных»: модуль ПДн не хранит намеренно, но обязан не логировать полный payload джоб, только метаданные.

Тройной гейт доступа

Главный риск модуля — случайное включение диагностики очередей/кеша в чужих руках на проде (retry/delete задач, сброс тега кеша могут деградировать прод). Поэтому доступ к панели защищён тройным независимым гейтом: все три условия обязательны одновременно, ни одно не даёт доступа само по себе, отказ любого — блокирует. Проверка — в middleware, до рендера страницы и повторно перед выполнением любого Filament action (не только на входе на страницу):

#СлойУсловиеОтказ этого слоя блокирует
1envAPP_ENV не production, либо production с явным дополнительным допуском парка (не выставляется по умолчанию)доступ на обычном проде без явного допуска
2настройка (kill-switch)dev-panel.enabled_for_studio_only=true и dev-panel.remote_disabled=false — второй флаг форсируется студией дистанционно через флаги managed-парка, клиент не может вернуть его в false самдоступ при принудительном отключении студией или снятой локальной настройке
3рольstudio-permission (dev-panel.view для чтения, dev-panel.manage для мутирующих action) у текущего пользователядоступ у пользователя без studio-роли

Следствия независимости слоёв: настройки в «разрешено» без studio-роли доступа не дают (слой 3 блокирует); studio-роль при dev-panel.remote_disabled=true доступа не даёт (слой 2 блокирует — так студия дистанционно закрывает панель на инсталляции, не трогая роли пользователей); прод без явного допуска блокирует независимо от настройки и роли (слой 1). Три отдельных контрактных теста на обход каждого слоя — «Тесты и приёмка».

Матрица ролей

Рольdev-panel.viewdev-panel.manage
studioдада
админ (клиент)нетнет
менеджернетнет
редакторнетнет

Обе permissions выдаются только studio-роли манифестом; остальным ролям панель не видна в меню админки, а прямое обращение к роуту отдаёт 403 (слой 3 гейта) независимо от состояния env и настроек.

UX-требования

Админ (studio-инженер — единственная аудитория): пустое состояние «failed jobs нет» — явный положительный сигнал, не пустая таблица без пояснения; массовое действие — retry всех failed jobs одним кликом (не по одной), с подтверждением при большом количестве; ошибки выполнения команды — вывод команды показывается как есть (это диагностический инструмент, детали важны), но обёрнут понятным заголовком «команда завершилась с ошибкой»; подтверждение перед retry/delete failed job — необратимое действие (delete), явное «да, удалить» отдельно от retry; карта модулей — визуально различает installed/enabled/disabled, не сваливает всё в один список; если панель недоступна из-за принудительного dev-panel.remote_disabled=true, диагностика (cms:dev-panel:doctor --json) называет причину явно — «отключено студией на managed-парке», а не обезличенным кодом ошибки, чтобы studio-инженер сразу понимал, какой слой гейта сработал.

Крайние случаи и типовые баги

  • случайное включение на проде → тройной гейт (env + kill-switch + роль, «Безопасность») на уровне middleware, не полагающийся ни на один фактор в отдельности — попытка открыть панель на проде без studio-роли возвращает 403 до рендера страницы, не частично отрендеренную панель с ограниченными правами;
  • whitelist команд обойдён через прямой вызов сервиса (не через Filament action) → сервис выполнения команды сам проверяет whitelist на входе, а не полагается на то, что UI не покажет других кнопок — тот же контракт вызывается и из теста, и из UI;
  • retry failed job, которая уже была удалена другим администратором (гонка двух studio-инженеров) → повторный retry несуществующего job_id — понятная ошибка «задача уже обработана/удалена», не исключение 500;
  • массовый retry огромного числа failed jobs одновременно → нагрузка на очередь скачком — предупреждение в UI при большом количестве («вы уверены, что хотите повторить N задач сразу?»), сама операция — через очередь пачками, не синхронным циклом в HTTP-запросе;
  • выключение модуля посреди выполнения команды → команда уже выполняется вне контекста включённости модуля (artisan-процесс не проверяет флаг повторно посередине) — доработка идёт до конца, панель просто пропадает из меню для новых действий;
  • сброс тега кеша, который активно используется другим модулем прямо сейчас → ручной сброс — штатная операция инвалидации (та же, что по событию), гонка с одновременной записью не хуже, чем обычная инвалидация по событию — доп. защиты не требуется сверх штатного поведения CacheTags;
  • пустая карта модулей (гипотетически невозможна — ядро всегда установлено, но крайний случай для тестов) → отображается корректно, не как ошибка;
  • огромная карта модулей (сотни модулей на инсталляции) → граф зависимостей должен оставаться читаемым — фильтрация/поиск по имени модуля, не единый нечитаемый список;
  • измерения locale/city/site — панель не работает с контентными данными и не несёт этих измерений; UI самой панели должен быть мультиязычен (админка мультиязычна по §8 стандарта), но это про интерфейс, не про данные;
  • противоречивая комбинация настроек: dev-panel.enabled_for_studio_only=false на managed-инсталляции не открывает доступ клиентским ролям сама по себе — слой «роль» тройного гейта («Безопасность») независимо блокирует пользователей без studio-permission даже при этой настройке; тем не менее снятие одного из трёх слоёв защиты — явное ослабление обороны и требует предупреждения в UI при сохранении («вы снижаете число независимых уровней защиты панели»), а не молчаливого применения;
  • dev-panel.remote_disabled=true, но studio-инженеру нужен экстренный доступ на проде → локальной настройкой клиента форс не снять (иначе это не kill-switch) — доступ восстанавливается только повторным действием студии через флаги managed-парка, а не правкой настройки на инсталляции.

Донорский код

Донор: — (новая разработка). Миграция legacy-данных (§16 стандарта) не применима: модуль не хранит собственных данных, донора с боевыми данными для переноса нет — импортировать нечего.

Тесты и приёмка

  • [ ] Контрактные тесты: список разрешённых команд — жёсткий whitelist, произвольная команда не выполняется;
  • [ ] health-чек модуля подтверждает, что деструктивные команды (migrate:fresh и т.п.) не входят в whitelist ни при какой конфигурации;
  • [ ] деградация при выключении — сайт работает без изменений, панель просто скрыта;
  • [ ] доступ жёстко ограничен studio-ролью, обычная админ-роль клиента панель не видит;
  • [ ] тройной гейт — три отдельных контрактных теста на обход, ни один фактор не достаточен сам по себе:
    • [ ] env-only обход не проходит: APP_ENV=production без явного допуска — 403, даже если enabled_for_studio_only=true, remote_disabled=false и у пользователя есть studio-роль;
    • [ ] setting-only обход не проходит: enabled_for_studio_only=true, remote_disabled=false на непроде — доступа нет без studio-роли (403);
    • [ ] role-only обход не проходит: studio-роль на пользователе не открывает доступ, если dev-panel.remote_disabled=true (kill-switch студии не обходится ролью);
  • [ ] retry/delete failed job не создаёт дублей задач в очереди;
  • [ ] retry уже удалённой задачи отдаёт понятную ошибку, не 500;
  • [ ] карта модулей строится без N+1 (один проход по реестру модулей);
  • [ ] контрактный набор cms-testing зелёный, пакет протестирован в testbench-изоляции;
  • [ ] feature-тест на каждый роут API; тестовая БД только dev-panel_test, migrate:fresh/refresh/reset запрещены.

Внутренняя база знаний студии