# Интеграция веб-админки Этот документ описывает запуск встроенного административного веб-API бота и типовой сценарий интеграции c внешней веб-админкой. API разворачивается вместе с ботом, использует FastAPI и защищено токенами доступа. ## 1. Обзор архитектуры - Веб-API запускается в том же процессе, что и бот, через встроенный `uvicorn` сервер. - Авторизация выполняется по токену: `X-API-Key` или `Authorization: Bearer `. - Все эндпоинты работают поверх 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://:/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://:`. ## 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` | Создать или обновить персональное промо-предложение пользователю. | `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.