Тема
ТЗ — Панель разработчика (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 job | job_id | permission 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/Redis | retry/delete конкретной задачи | прямой вызов API Horizon (только safe-операции) |
CacheTags ядра | сброс конкретного тега | вызов контракта ядра, не raw Cache::flush() |
Настройки (группа dev-panel)
| Ключ | Тип | Дефолт | affectsPageCache | Описание |
|---|---|---|---|---|
dev-panel.allowed_commands | array | ["cache:clear","view:clear","config:clear","queue:restart"] | — | Whitelist команд, доступных из UI |
dev-panel.failed_jobs_page_size | int | 25 | — | Размер страницы списка failed jobs |
dev-panel.enabled_for_studio_only | bool | true | — | Один из трёх независимых слоёв гейта (см. «Безопасность») — сама по себе не гарантирует ограничение доступа, роль и env проверяются отдельно |
dev-panel.remote_disabled | bool | false | — | Kill-switch managed-парка: студия форсирует true дистанционно через флаги парка; клиент не может выставить false сам, пока студия не снимет форс — третий слой гейта |
dev-panel.command_log_retention_days | int | 7 | — | Хранение лога вывода запущенных команд |
dev-panel.max_bulk_retry | int | 50 | — | Порог количества задач, выше которого массовый 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-команды из UI | command, user_id, result |
DevPanelCacheTagFlushed | Ручной сброс тега кеша из инспектора | tag, user_id |
result в DevPanelCommandExecuted — статус завершения и код возврата команды, не сырой вывод целиком (сырой вывод — только в файловом логе модуля со своим ретеншном, не в событии, чтобы не тиражировать потенциально чувствительный текст по слушателям события).
Provides-контрактов не реализует и не потребляет. Читает реестр модулей и их requires/suggests/provides через контракты ядра (не сырыми запросами) для карты зависимостей. Слушает только собственные UI-действия (запуск команды, сброс тега).
Таблица взаимодействий
| Сущность/модуль | Канал | Направление | Что происходит |
|---|---|---|---|
| Реестр модулей ядра | чтение через cms/core-contracts | in | Построение карты зависимостей 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не0→cms:dev-panel:doctor --json.
- панель доступна пользователю без studio-роли на проде → немедленный аудит (кто получил доступ и когда, не форсирован ли
Производительность и кеш
Объёмы: количество 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 (не только на входе на страницу):
| # | Слой | Условие | Отказ этого слоя блокирует |
|---|---|---|---|
| 1 | env | APP_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.view | dev-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 студии не обходится ролью);
- [ ] env-only обход не проходит:
- [ ] retry/delete failed job не создаёт дублей задач в очереди;
- [ ] retry уже удалённой задачи отдаёт понятную ошибку, не 500;
- [ ] карта модулей строится без N+1 (один проход по реестру модулей);
- [ ] контрактный набор
cms-testingзелёный, пакет протестирован в testbench-изоляции; - [ ] feature-тест на каждый роут API; тестовая БД только
dev-panel_test,migrate:fresh/refresh/resetзапрещены.