Files
remnawave-bedolaga-telegram…/docs/web-admin-integration.md
2025-12-05 09:56:03 +03:00

290 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Интеграция веб-админки
Этот документ описывает запуск встроенного административного веб-API бота и типовой сценарий интеграции c внешней веб-админкой.
API разворачивается вместе с ботом, использует FastAPI и защищено токенами доступа.
## 1. Обзор архитектуры
- Веб-API запускается в том же процессе, что и бот, через встроенный `uvicorn` сервер.
- Авторизация выполняется по токену: `X-API-Key` или `Authorization: Bearer <token>`.
- Все эндпоинты работают поверх HTTPS/HTTP и возвращают структуры в формате JSON.
- Встроенный механизм миграций создаёт таблицу `web_api_tokens` и бутстрап-токен, если указан в конфигурации.
## 2. Настройка окружения
Добавьте переменные в `.env` (или другую систему конфигурации):
| Переменная | Назначение | Значение по умолчанию / пример |
|------------|------------|---------------------------------|
| `WEB_API_ENABLED` | Включает веб-API. | `true`
| `WEB_API_HOST` | IP/hostname, на котором слушает API. | `0.0.0.0`
| `WEB_API_PORT` | Порт веб-API. | `8080`
| `WEB_API_ALLOWED_ORIGINS` | Список доменов для CORS, через запятую. `*` разрешит всё. | `https://admin.example.com`
| `WEB_API_DOCS_ENABLED` | Включить `/docs`, `/doc` (редирект), `/redoc` и `/openapi.json`. В проде лучше `false`. | `false`
| `WEB_API_WORKERS` | Количество воркеров uvicorn. В embed-режиме всегда приводится к `1`. | `1`
| `WEB_API_REQUEST_LOGGING` | Логировать каждый запрос API. | `true`
| `WEB_API_DEFAULT_TOKEN` | Бутстрап-токен, который будет создан при миграции. | `super-secret-token`
| `WEB_API_DEFAULT_TOKEN_NAME` | Отображаемое имя созданного токена. | `Bootstrap Token`
| `WEB_API_TOKEN_HASH_ALGORITHM` | Алгоритм хеширования токенов (`sha256`, `sha512`, ...). | `sha256`
> ⚠️ Если вы храните конфигурацию в Kubernetes/Ansible/других системах — не забудьте обновить секреты, чтобы бот видел эти переменные.
### Включение Swagger (интерактивной документации)
Чтобы открыть интерфейс Swagger UI на `/docs`, убедитесь, что одновременно заданы две переменные окружения:
1. `WEB_API_ENABLED=true` — включает само веб-API.
2. `WEB_API_DOCS_ENABLED=true` — публикует `/docs`, `/doc` (редирект для старых ссылок), `/redoc` и `/openapi.json`.
После изменения значений перезапустите бота. Интерфейс будет доступен по адресу `http://<WEB_API_HOST>:<WEB_API_PORT>/docs`.
## 3. Подготовка базы данных
1. Убедитесь, что настройки БД верны (`DATABASE_URL` или параметры PostgreSQL/SQLite).
2. При старте бота автоматически запускается универсальная миграция `run_universal_migration`, которая:
- создаёт таблицу `web_api_tokens`, если её нет;
- активирует токен из `WEB_API_DEFAULT_TOKEN`, если он задан.
3. Если нужно запустить миграцию вручную, выполните:
```bash
python -c "import asyncio; from app.database.universal_migration import run_universal_migration; asyncio.run(run_universal_migration())"
```
Или просто запустите `python main.py` — бот выполнит ту же процедуру автоматически.
## 4. Запуск веб-API
```bash
# Создаём .env и включаем веб-API
cp .env.example .env
nano .env # проставьте WEB_API_* переменные и BOT_TOKEN
# Запускаем бота (локально)
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python main.py
```
В Docker достаточно пробросить порт `WEB_API_PORT` из контейнера бота. После запуска API будет доступно по адресу `http://<WEB_API_HOST>:<WEB_API_PORT>`.
## 5. Аутентификация и токены
- Первый токен удобно задать через `WEB_API_DEFAULT_TOKEN`. Он появится в таблице при запуске миграции и будет автоматически
пересоздан/активирован после изменения значения через интерфейс настроек.
- Для управления токенами используйте эндпоинты `/tokens`:
- `GET /tokens` — список токенов.
- `POST /tokens` — создать новый токен. Возвращает открытое значение один раз.
- `POST /tokens/{id}/revoke` и `/activate` — управление статусом.
- `DELETE /tokens/{id}` — удаление.
- Заголовок авторизации можно передавать двумя способами:
```http
X-API-Key: <ваш_токен>
# или
Authorization: Bearer <ваш_токен>
```
Пример запроса на создание токена:
```bash
curl -X POST "http://127.0.0.1:8080/tokens" \
-H "X-API-Key: super-secret-token" \
-H "Content-Type: application/json" \
-d '{"name": "Web admin", "description": "UI token"}'
```
## 6. Основные эндпоинты
| Метод | Путь | Назначение |
|-------|------|------------|
| `GET` | `/health` | Статус API, версия бота, флаги включённых сервисов.
| `GET` | `/stats/overview` | Сводная статистика по пользователям, подпискам, платежам и тикетам.
| `GET` | `/settings/categories` | Категории системных настроек.
| `GET` | `/settings` | Полный список настроек (с текущими и дефолтными значениями).
| `GET` | `/settings/{key}` | Получить одну настройку.
| `PUT` | `/settings/{key}` | Обновить значение настройки.
| `DELETE` | `/settings/{key}` | Сбросить настройку к значению по умолчанию.
| `GET` | `/users` | Список пользователей с фильтрами и пагинацией.
| `GET` | `/users/{id}` | Детали пользователя. ID может быть как внутренним (user.id), так и Telegram ID (user.telegram_id).
| `POST` | `/users` | Создать пользователя (например, для ручной выдачи доступа).
| `PATCH` | `/users/{id}` | Обновить профиль пользователя или статус. ID может быть как внутренним (user.id), так и Telegram ID (user.telegram_id).
| `POST` | `/users/{id}/balance` | Корректировка баланса с созданием транзакции. ID может быть как внутренним (user.id), так и Telegram ID (user.telegram_id).
| `GET` | `/subscriptions` | Список подписок с фильтрами.
| `POST` | `/subscriptions` | Создать триальную или платную подписку.
| `POST` | `/subscriptions/{id}/extend` | Продлить подписку на N дней.
| `POST` | `/subscriptions/{id}/traffic` | Добавить трафик (ГБ).
| `POST` | `/subscriptions/{id}/devices` | Добавить устройства.
| `POST` | `/subscriptions/{id}/squads` | Привязать сквад.
| `DELETE` | `/subscriptions/{id}/squads/{uuid}` | Удалить сквад.
| `GET` | `/transactions` | История транзакций.
| `GET` | `/tickets` | Список тикетов поддержки.
| `GET` | `/tickets/{id}` | Тикет с перепиской.
| `POST` | `/tickets/{id}/status` | Изменить статус тикета.
| `POST` | `/tickets/{id}/priority` | Изменить приоритет.
| `POST` | `/tickets/{id}/reply-block` | Заблокировать ответы пользователя.
| `DELETE` | `/tickets/{id}/reply-block` | Снять блокировку.
| `GET` | `/promo-groups` | Список промо-групп с количеством участников.
| `POST` | `/promo-groups` | Создать промо-группу.
| `PATCH` | `/promo-groups/{id}` | Обновить промо-группу.
| `DELETE` | `/promo-groups/{id}` | Удалить промо-группу.
| `GET` | `/promo-offers` | Список промо-предложений с фильтрами по пользователю, статусу и типу уведомления.
| `POST` | `/promo-offers` | Создать или обновить персональное промо-предложение пользователю. ID может быть как внутренним (user.id), так и Telegram ID (user.telegram_id).
| `GET` | `/promo-offers/{id}` | Детали конкретного промо-предложения.
| `GET` | `/promo-offers/templates` | Список шаблонов промо-предложений.
| `GET` | `/promo-offers/templates/{id}` | Получить данные шаблона промо-предложения.
| `PATCH` | `/promo-offers/templates/{id}` | Обновить текст, кнопки и параметры шаблона.
| `GET` | `/promo-offers/logs` | Журнал операций с промо-предложениями (активации, списания, выключения).
| `GET` | `/tokens` | Управление токенами доступа.
| `GET` | `/polls` | Список опросов с постраничной навигацией.
| `GET` | `/polls/{id}` | Детали опроса с вопросами и вариантами ответов.
| `POST` | `/polls` | Создать опрос: заголовок, описание, вопросы и варианты.
| `DELETE` | `/polls/{id}` | Удалить опрос целиком.
| `GET` | `/polls/{id}/stats` | Сводная статистика по ответам и начисленным наградам.
| `GET` | `/polls/{id}/responses` | Ответы пользователей с детализацией по вопросам.
| `GET` | `/logs/monitoring` | Логи мониторинга бота с пагинацией и фильтрами по типу события.
| `GET` | `/logs/monitoring/event-types` | Справочник доступных типов событий мониторинга.
| `GET` | `/logs/support` | Журнал действий модераторов поддержки (блокировки, закрытия тикетов).
| `GET` | `/logs/support/actions` | Справочник возможных действий в аудите поддержки.
| `GET` | `/logs/system` | Предпросмотр системного лог-файла бота с метаданными.
| `GET` | `/logs/system/download` | Скачивание полного лог-файла бота (`text/plain`).
> Раздел **promo-offers** в Swagger объединяет работу с персональными предложениями: выдачу скидок/бонусов пользователям, настройку
> текстов шаблонов и просмотр журнала операций (активации, автосписания, отключения просроченных акций).
### Логи бота
В административном API появился раздел **logs**. Он позволяет:
- Просматривать общие логи мониторинга (`GET /logs/monitoring`) с поддержкой пагинации (`limit`, `offset`) и фильтра по типу события (`event_type`).
- Получать справочник доступных типов событий (`GET /logs/monitoring/event-types`). Это удобно для построения фильтров во внешней админке.
- Отслеживать действия модераторов поддержки (`GET /logs/support`) с пагинацией и возможностью фильтровать по конкретному действию (`action`).
- Запрашивать список возможных действий для UI (`GET /logs/support/actions`).
- Просматривать системный лог-файл бота (`GET /logs/system`). Endpoint возвращает метаданные (путь, время изменения, размер в байтах/символах) и фрагмент конца файла, размер которого можно регулировать параметром `preview_limit` (от 500 до 20 000 символов).
- Скачивать полный системный лог в текстовом формате (`GET /logs/system/download`).
Все эндпоинты защищены токеном API и возвращают структуру с общим количеством записей, текущим `limit`/`offset` и массивом объектов. Это упрощает реализацию таблиц и постраничной навигации во внешних административных интерфейсах.
### Управление опросами
Раздел **polls** в административном API позволяет создавать и анализировать опросы, которые бот рассылает пользователям.
#### Список и детали
- `GET /polls` — возвращает массив объектов с базовой информацией: название, описание, флаги награды, количество вопросов и ответов.
- `GET /polls/{id}` — раскрывает структуру конкретного опроса, включая упорядоченные вопросы и варианты ответов. Подходит для предпросмотра перед публикацией.
#### Создание опроса
Для создания опроса отправьте JSON, соответствующий схеме:
```json
{
"title": "Оценка нового тарифа",
"description": "Помогите улучшить продукт — ответ займет до 2 минут",
"reward_enabled": true,
"reward_amount_kopeks": 1000,
"questions": [
{
"text": "Насколько вы довольны скоростью соединения?",
"options": [
{ "text": "Очень доволен" },
{ "text": "Скорее доволен" },
{ "text": "Нейтрально" },
{ "text": "Скорее недоволен" },
{ "text": "Очень недоволен" }
]
},
{
"text": "Какие улучшения вы ждёте?",
"options": [
{ "text": "Стабильность" },
{ "text": "Скорость" },
{ "text": "Поддержка" }
]
}
]
}
```
Требования валидации:
- Заголовок (`title`) — от 1 до 255 символов, не пустой после обрезки пробелов.
- Описание (`description`) — до 4000 символов, пробелы по краям удаляются.
- Если `reward_enabled=true`, сумма вознаграждения (`reward_amount_kopeks`) должна быть положительной. При `false` значение автоматически сбрасывается в `0`.
- Каждый вопрос содержит минимум два уникальных варианта ответа.
В ответ API вернёт созданный опрос с назначенными идентификаторами и полем `reward_amount_rubles`, которое удобно показывать в интерфейсе.
#### Удаление и статистика
- `DELETE /polls/{id}` — удаляет опрос и связанные с ним вопросы/ответы. Используйте с осторожностью, операция необратима.
- `GET /polls/{id}/stats` — агрегированная статистика: общее количество ответов, завершённых прохождений и сумма выданных наград. Для каждого вопроса возвращается количество выборов по вариантам.
- `GET /polls/{id}/responses` — список ответов пользователей с пагинацией (`limit`, `offset`). Каждый элемент содержит временные метки (`sent_at`, `started_at`, `completed_at`), данные пользователя (ID, username, Telegram ID), информацию о выданной награде и массив ответов с текстами вопросов/вариантов.
Такой формат позволяет без дополнительного запроса показать детализацию на фронтенде или выгрузить данные в CSV.
### RemnaWave интеграция
После включения веб-API в Swagger (`WEB_API_DOCS_ENABLED=true`) появится раздел **remnawave**. Он объединяет эндпоинты для управления панелью RemnaWave и синхронизации данных бота:
| Метод | Путь | Назначение |
|-------|------|------------|
| `GET` | `/remnawave/status` | Проверка конфигурации и доступности RemnaWave API. |
| `GET` | `/remnawave/system` | Агрегированная статистика по пользователям, нодам и трафику. |
| `GET` | `/remnawave/nodes` | Список нод и их текущее состояние. |
| `GET` | `/remnawave/nodes/realtime` | Текущая загрузка нод (realtime-метрики RemnaWave). |
| `GET` | `/remnawave/nodes/{uuid}` | Детальная информация по конкретной ноде. |
| `GET` | `/remnawave/nodes/{uuid}/statistics` | Агрегированная статистика и история нагрузок по ноде. |
| `GET` | `/remnawave/nodes/{uuid}/usage` | История использования ноды пользователями за выбранный период. |
| `POST` | `/remnawave/nodes/{uuid}/actions` | Включение, отключение или перезапуск ноды. |
| `POST` | `/remnawave/nodes/restart` | Массовый перезапуск всех нод в RemnaWave. |
| `GET` | `/remnawave/squads` | Список внутренних сквадов с составом и статистикой. |
| `GET` | `/remnawave/squads/{uuid}` | Детали выбранного сквада. |
| `POST` | `/remnawave/squads` | Создание нового сквада и привязка inbounds. |
| `PATCH` | `/remnawave/squads/{uuid}` | Обновление имени или состава inbounds сквада. |
| `POST` | `/remnawave/squads/{uuid}/actions` | Массовые операции: добавить/удалить всех, переименовать, обновить inbounds, удалить. |
| `GET` | `/remnawave/inbounds` | Список доступных inbounds в панели RemnaWave. |
| `GET` | `/remnawave/users/{telegram_id}/traffic` | Использование трафика конкретного пользователя RemnaWave. |
| `POST` | `/remnawave/sync/from-panel` | Синхронизация пользователей и подписок из панели в бота. |
| `POST` | `/remnawave/sync/to-panel` | Обратная синхронизация данных бота в панель. |
| `POST` | `/remnawave/sync/subscriptions/validate` | Проверка и восстановление подписок в RemnaWave. |
| `POST` | `/remnawave/sync/subscriptions/cleanup` | Очистка «осиротевших» подписок и пользователей в RemnaWave. |
| `POST` | `/remnawave/sync/subscriptions/statuses` | Приведение статусов подписок в боте и панели к единому виду. |
| `GET` | `/remnawave/sync/recommendations` | Рекомендации по синхронизации: что добавить, обновить или удалить. |
> Все списковые эндпоинты поддерживают пагинацию (`limit`, `offset`) и фильтры, описанные в OpenAPI спецификации. Если `WEB_API_DOCS_ENABLED=true`, документация доступна по `/docs`. В ответах `/settings` поле `choices` всегда массив: пустой список означает отсутствие предопределённых значений.
## 7. Сценарий интеграции веб-админки
1. **Health-check** — перед авторизацией UI вызывает `GET /health`, чтобы отобразить статус и версию бота.
2. **Настройки UI** — подгружает категории через `GET /settings/categories`, далее выводит форму со значениями из `GET /settings`.
3. **Статистика дашборда**`GET /stats/overview` для карточек с показателями.
4. **Раздел пользователи**`GET /users` с поиском (`search`), фильтрами по статусу или промо-группе. Для детальной карточки использовать `GET /users/{id}` (в качестве ID можно использовать как внутренний user.id, так и telegram_id пользователя).
5. **Операции с подпиской** — использовать `POST /subscriptions/{id}/...` эндпоинты для продления, выдачи трафика и устройств.
6. **Поддержка** — список тикетов (`GET /tickets`), изменение статуса (`POST /tickets/{id}/status`), блокировка ответов (`POST /tickets/{id}/reply-block`).
7. **История операций**`GET /transactions` с фильтрами по пользователю, типу и периоду.
## 8. CORS, безопасность и логирование
- Разрешённые домены указываются в `WEB_API_ALLOWED_ORIGINS`. Для нескольких доменов перечислите их через запятую.
- Для продакшена рекомендуется отключить публичную документацию (`WEB_API_DOCS_ENABLED=false`).
- `WEB_API_REQUEST_LOGGING=true` добавляет middleware, которое логирует метод, путь и статус ответа. Используйте его для аудита или отключите в продакшене, если хватает reverse-proxy логов.
- Все токены хранятся в базе в хешированном виде. Не храните открытые значения в коде.
## 9. Диагностика проблем
| Симптом | Возможная причина | Что проверить |
|---------|------------------|---------------|
| 401 Unauthorized | Неверный или просроченный токен. | Пересоздайте токен через `/tokens` и обновите UI. |
| 403/404 при работе с настройками | Неверный ключ настройки. | Получите список доступных ключей через `GET /settings`. |
| 422 Unprocessable Entity | Неверный тип данных в теле запроса. | Проверьте типы (числа, булевы, строки) и формат JSON. |
| API не стартует | Порт занят или неверные переменные окружения. | Проверьте логи контейнера бота и значения `WEB_API_HOST/PORT`. |
## 10. Рекомендации по эксплуатации
- Регулярно ревизируйте список активных токенов и отключайте неиспользуемые.
- Для внешних админок размещайте API за reverse-proxy (nginx, Caddy, Traefik) с TLS.
- Включите мониторинг доступности (например, curl на `/health`) в систему наблюдения.
- Обновляйте бота и админку синхронно, чтобы использовать новые поля API.