> ## 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` примонтирован для сохранения медиа между перезапусками.
</Note>

***

## Управление статьями

### Создание статьи

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>
  Счётчик просмотров скрыт от обычных пользователей и виден только администраторам.
</Note>

***

## RBAC права

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

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

Настройка прав: [RBAC](/cabinet/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       |
