Один кабинет для команды и админа. Не отдельная «CMS», не панель на React. PHP, SQLite, права, статусы и предпросмотр — минимально достаточно, чтобы не мешать работать.
Почему одна админка
Сначала хотелось разделить: «админка» для админа, «личный кабинет» для участника. На практике это два одинаковых интерфейса, которые всё время расходятся.
В итоге остался один кабинет с разными возможностями:
- Админ видит всё: участников, проекты, статьи, блог, музыку, серверы, бейджи, репорты, настройки, аудит.
- Участник видит только своё: профиль, свои проекты, свои записи, свои статьи.
- Общие разделы — дашборд и профиль — есть у всех, но наполнение зависит от роли.
Это не «идеальная RBAC-система». Это практичный компромисс: одна кодовая база, один набор компонентов, одна логика входа.
Роли и меню
Роль — одно поле users.role: admin или member. Этого хватает. Не потому что сложные роли не нужны, а потому что пока не нужны. Если появятся редакторы, модераторы и «только просмотр», тогда будет отдельная матрица прав. Сейчас — два уровня.
Меню строится из массива $pages в admin/index.php. Там же — маршрут, заголовок и флаг «только для админа». А вкладки в шапке — отдельный массив $navAdmin в admin/pages/layout_top.php.
И вот первая грабля: навигация живёт в двух местах. Добавляешь раздел — надо не забыть оба. Один раз забыл: страница есть, роут есть, а вкладки нет. Пользователь до неё не дойдёт. С тех пор в проектной памяти записано: новый раздел — править $pages и $navAdmin.
Почему не «просто форма»
Админка не должна быть набором форм «сохранить/удалить». Иначе она начинает мешать: слишком много кликов, слишком мало контекста.
Что реально помогает:
- Дашборд со счётчиками: сколько статей на проверке, сколько репортов, сколько участников, что происходит.
- Фильтры и поиск в списках. Не надо листать всю таблицу, чтобы найти одного человека.
- Статусы прямо в списке. Видно, что черновик, что на проверке, что опубликовано.
- Быстрые действия — опубликовать, снять, отклонить, вернуть в черновики.
- Модалка с полным текстом там, где строки мало: репорт, очередь Telegram, статья.
- Аудит. Кто что сделал и когда. Не для слежки, а чтобы разбираться в инцидентах.
Админка — это продукт для команды. Если в ней больно работать, команда пишет в чат «сделай за меня», и админ становится бутылочным горлышком.
Модерация: черновик → проверка → публикация
У статьи есть внутренние статусы:
draft— черновик;pending— отправлена на проверку;published— опубликована;rejected— отклонена с причиной.
Публично виден только published. Всё остальное — 404, кроме автора, админа и ссылки предпросмотра.
Автор пишет в «Мои статьи», сохраняет сколько угодно, смотрит предпросмотр. Когда готов — отправляет на проверку. Админ читает, одобряет или отклоняет. При отклонении автор видит причину и может исправить.
Кнопка «Снять с публикации» возвращает статью в draft. Не в «скрытую», не в «unlisted» — в обычный черновик. Об этом отдельная статья, но принцип важен: меньше состояний — меньше путаницы.
Почему «Опубликованные» не смешиваются с «На проверке»
Была ошибка: в кабинете статьи со статусом pending попадали в список «Опубликованные». Автор видел статью в опубликованных и думал, что она уже вышла. А её ещё никто не проверил.
Пришлось разделить на три списка:
- Черновики —
draft+rejected; - На проверке —
pending; - Опубликованные —
published.
Теперь статус совпадает с тем, что человек видит. Мелочь, но именно такие мелочи создают доверие к интерфейсу.
Предпросмотр по токену
Черновик нельзя показывать всем. Но иногда нужно показать его человеку: редактору, автору, заказчику.
Для этого у статьи есть preview_token. Ссылка вида:
/article/?id=42&preview=8f3c...
работает для черновика и не индексируется. Токен можно перевыпустить. Если статья опубликована, предпросмотр не нужен — работает обычная ссылка.
Это лучше, чем «временная публикация»: не надо менять статус, не надо потом не забыть снять. Ссылка либо есть, либо нет.
Права и проверки
Права проверяются не только в меню, но и в обработчиках. Меню можно скрыть, но запрос всё равно можно отправить руками. Поэтому:
- каждая запись проверяет
is_admin()или авторство; - каждое действие проверяет CSRF;
- вход — по паролю, сессии — в реестре;
- действия пишутся в аудит;
- удаление — отдельное действие, а не «сохранить с пустым полем».
Последнее особенно важно. Если форма редактирования приходит с пустым полем, это не значит «удалить». Это может быть сбой, недогрузка или злой умысел. Удаление должно быть явным.
Где мы наступили на грабли
Тест обработчика через CLI пишет в базу
Один раз я тестировал обработчик, подставив $_POST вручную. Он честно сохранил данные. В том числе затёр README проекта: вместо нормального текста осталось x. Восстанавливали из бэкапа.
Урок: обработчик не знает, что он «тест». Если он пишет в базу — он пишет по-настоящему. Для проверок — отдельная запись или снапшот полей до/после.
Модалки вне <main>
Когда искал элементы для PJAX, смотрел только до первого <script>. А модалки README лежали после скриптов, вне <main>. В итоге переходы без перезагрузки их ломали.
Урок: структура страницы важнее, чем кажется. Если что-то живёт вне основного контейнера, это надо знать.
Кэш компонентов
Шапка и футер вставляются JS. Если поправить components/footer.html, но не поднять версию компонента и версию page-init.js в HTML, браузер держит старый футер. Ссылка «есть только на главной» — классический симптом.
Урок: версия — часть компонента. Нет версии — нет гарантии, что пользователь увидит правку.
Что бы я улучшил
- Сделал бы нормальную матрицу прав, когда появятся роли кроме админа и участника.
- Добавил бы «историю версий» для статей и проектов, а не только аудит.
- Сделал бы массовые действия не только для анимации, но и для статей.
- Убрал бы оставшиеся места, где навигация дублируется.
- Добавил бы тесты на обработчики с временной БД, чтобы не трогать боевую.
Но даже в текущем виде кабинет делает главное: не мешает. Открыл, нашёл, поправил, отправил. Без «сначала изучи фреймворк».
Вместо вывода
Хорошая админка — скучная. В ней понятные статусы, предсказуемые кнопки и нет сюрпризов.
Самое сложное — не написать формы. Самое сложное — не добавить лишних состояний и не спрятать важное за тремя кликами. Если команда пользуется кабинетом без инструкции, значит, он получился.
