Обзор
Модуль новостей позволяет публиковать статьи для пользователей прямо в Cabinet. Администраторы создают статьи с WYSIWYG-редактором, загружают медиа, управляют категориями и тегами. Пользователи видят ленту новостей в Cabinet с фильтрацией по категориям.
Возможности
- Полноценный WYSIWYG-редактор (TipTap) с поддержкой изображений и видео
- Управляемые категории и теги с цветовой маркировкой
- Загрузка медиа: drag-drop, вставка из буфера, файловый пикер
- Избранная статья — автоматически снимается с остальных при назначении
- Счётчик просмотров с дедупликацией (5 мин на пользователя)
- Автогенерация slug из заголовка (транслитерация кириллицы)
- Серверная санитизация HTML-контента (XSS-защита)
- RBAC-права:
news:read, news:create, news:edit, news:delete
Настройка
Переменные окружения
Модуль новостей использует настройки медиа-загрузки:
| Переменная | Тип | По умолчанию | Описание |
|---|
MEDIA_UPLOAD_DIR | str | ./uploads | Директория для загрузок |
MEDIA_MAX_IMAGE_SIZE_MB | int | 10 | Макс. размер изображения (МБ) |
MEDIA_MAX_VIDEO_SIZE_MB | int | 50 | Макс. размер видео (МБ) |
MEDIA_IMAGE_MAX_DIMENSION | int | 2048 | Макс. сторона изображения (пиксели) |
MEDIA_JPEG_QUALITY | int | 85 | Качество JPEG-сжатия (1-100) |
Docker
В Dockerfile автоматически создаются поддиректории: uploads/images/, uploads/videos/, uploads/thumbnails/.
При использовании Docker убедитесь, что volume ./uploads примонтирован для сохранения медиа между перезапусками.
Управление статьями
Создание статьи
Admin Cabinet → Новости → Создать статью
| Поле | Описание | Ограничения |
|---|
| Заголовок | Заголовок статьи | 1-500 символов, обязательно |
| Slug | URL-идентификатор | Авто из заголовка, уникальный, [a-zA-Z0-9_-] |
| Контент | HTML-содержимое (WYSIWYG) | До 500 000 символов |
| Превью | Короткий текст для списка | До 1000 символов |
| Категория | Выбор из управляемого списка | Обязательно |
| Тег | Выбор из управляемого списка | Опционально |
| Обложка | URL изображения-баннера | http/https, до 2000 символов |
| Время чтения | Оценка в минутах | 1-60, по умолчанию 1 |
| Опубликовать | Видимость для пользователей | По умолчанию — черновик |
| Избранная | Закреплённая статья (одна) | Снимается с остальных автоматически |
Редактор контента
Редактор построен на TipTap (Prosemirror) и поддерживает:
- Форматирование: жирный, курсив, подчёркивание, зачёркнутый
- Заголовки H1-H3
- Списки (нумерованные и маркированные)
- Ссылки
- Изображения и видео (через загрузку медиа)
- Блоки кода
Публикация
При первой публикации автоматически устанавливается published_at — дата, по которой сортируется лента. Повторное снятие и публикация не меняют эту дату.
Избранная статья
Только одна статья может быть отмечена как избранная. При назначении новой избранной все остальные автоматически теряют этот статус (атомарная транзакция).
Категории и теги
Категории
Управляемые категории с цветовой маркировкой. Используются для фильтрации ленты новостей.
| Поле | Описание | Ограничения |
|---|
| Название | Название категории | 1-100 символов, уникальное (без учёта регистра) |
| Цвет | HEX-цвет для бейджа | По умолчанию #00e5a0 |
При удалении категории все статьи с ней теряют привязку (category_id=NULL).
Теги
Управляемые теги с цветовой маркировкой. Одна статья может иметь один тег.
| Поле | Описание | Ограничения |
|---|
| Название | Название тега | 1-50 символов, уникальное (без учёта регистра) |
| Цвет | HEX-цвет для бейджа | По умолчанию #94a3b8 |
При удалении тега все статьи с ним теряют привязку (tag_id=NULL).
Загрузка медиа
Поддерживаемые форматы
| Формат | Типы | Макс. размер | Обработка |
|---|
| Изображения | JPEG, PNG, WebP | 10 МБ | Конвертация в JPEG, ресайз до 2048px, thumbnail 400x400 |
| Видео | MP4, WebM | 50 МБ | Сохраняется как есть |
Обработка изображений
- Определение формата по magic bytes (не по расширению)
- Исправление EXIF-ориентации (фотографии с телефона)
- Нормализация в RGB
- Ресайз если сторона > 2048 px (с сохранением пропорций, LANCZOS)
- Сжатие JPEG с заданным качеством
- Генерация thumbnail 400×400 px
Безопасность загрузки
- Определение типа по magic bytes (не MIME sniffing)
- Защита от decompression bomb (макс. 25M пикселей)
- UUID-имена файлов (без пользовательского ввода)
- Атомарная запись (
.tmp → rename)
- Защита от path traversal при удалении
- HEIC/HEIF отклоняются (формат изображений, не видео)
Лента новостей (для пользователей)
Пользователи видят ленту опубликованных статей в Cabinet:
- Сортировка по дате публикации (новые сверху)
- Фильтрация по категориям
- Пагинация (до 100 статей на страницу)
- Счётчик просмотров (с дедупликацией: 1 просмотр на пользователя за 5 минут)
- Полный контент статьи при открытии по slug
Счётчик просмотров скрыт от обычных пользователей и виден только администраторам.
RBAC права
Если включён RBAC, для управления новостями требуются права:
| Право | Описание |
|---|
news:read | Просмотр статей, категорий, тегов |
news:create | Создание статей, категорий, тегов |
news:edit | Редактирование статей, категорий, тегов; загрузка медиа |
news:delete | Удаление статей, категорий, тегов; удаление медиа |
Настройка прав: RBAC
API эндпоинты
Админские
| Метод | Путь | Описание |
|---|
| GET | /admin/news | Список статей (включая черновики) |
| GET | /admin/news/{id} | Детали статьи |
| POST | /admin/news | Создать статью |
| PUT | /admin/news/{id} | Обновить статью |
| DELETE | /admin/news/{id} | Удалить статью |
| POST | /admin/news/{id}/publish | Переключить публикацию |
| POST | /admin/news/{id}/feature | Переключить избранную |
| GET | /admin/news/categories | Список категорий |
| POST | /admin/news/categories | Создать категорию |
| PUT | /admin/news/categories/{id} | Обновить категорию |
| DELETE | /admin/news/categories/{id} | Удалить категорию |
| GET | /admin/news/tags | Список тегов |
| POST | /admin/news/tags | Создать тег |
| PUT | /admin/news/tags/{id} | Обновить тег |
| DELETE | /admin/news/tags/{id} | Удалить тег |
| POST | /admin/news/media/upload | Загрузить медиа |
| DELETE | /admin/news/media/{filename} | Удалить медиа |
Публичные (для пользователей)
| Метод | Путь | Описание |
|---|
| GET | /news | Лента опубликованных статей |
| GET | /news/categories | Список категорий |
| GET | /news/{slug} | Полная статья по slug |