> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bedolagam.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# Модуль новостей

> Создание, управление и публикация новостей в Cabinet и боте

## Обзор

Модуль новостей позволяет публиковать статьи для пользователей прямо в 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/`.

> [!NOTE]
>
> При использовании 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 МБ | Сохраняется как есть |

### Обработка изображений

1. Определение формата по magic bytes (не по расширению)
2. Исправление EXIF-ориентации (фотографии с телефона)
3. Нормализация в RGB
4. Ресайз если сторона > 2048 px (с сохранением пропорций, LANCZOS)
5. Сжатие JPEG с заданным качеством
6. Генерация thumbnail 400×400 px

### Безопасность загрузки

* Определение типа по magic bytes (не MIME sniffing)
* Защита от decompression bomb (макс. 25M пикселей)
* UUID-имена файлов (без пользовательского ввода)
* Атомарная запись (`.tmp` → `rename`)
* Защита от path traversal при удалении
* HEIC/HEIF отклоняются (формат изображений, не видео)

***

## Лента новостей (для пользователей)

Пользователи видят ленту опубликованных статей в Cabinet:

* Сортировка по дате публикации (новые сверху)
* Фильтрация по категориям
* Пагинация (до 100 статей на страницу)
* Счётчик просмотров (с дедупликацией: 1 просмотр на пользователя за 5 минут)
* Полный контент статьи при открытии по slug

> [!NOTE]
>
> Счётчик просмотров скрыт от обычных пользователей и виден только администраторам.

***

## RBAC права

Если включён RBAC, для управления новостями требуются права:

| Право | Описание |
| - | - |
| `news:read` | Просмотр статей, категорий, тегов |
| `news:create` | Создание статей, категорий, тегов |
| `news:edit` | Редактирование статей, категорий, тегов; загрузка медиа |
| `news:delete` | Удаление статей, категорий, тегов; удаление медиа |

Настройка прав: [RBAC](/cabinet/rbac.md)

***

## 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 |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.