Files
vesti/openspec/changes/publisher-service/tasks.md
T

5.4 KiB
Raw Blame History

Tasks: publisher-service

1. Скелет сервиса

  • 1.1 Создать services/publisher/ (app/, requirements.txt, .env.example, README.md) Проверка: ls services/publisher/app/ → main.py, config.py, telegram.py, channels.py
  • 1.2 requirements.txt: fastapi, uvicorn, httpx[socks], pydantic, python-dotenv Проверка: .venv/bin/pip install -r services/publisher/requirements.txt без ошибок
  • 1.3 config.py: env VESTI_BOT_TOKEN, TG_PROXY, VESTI_BOT_CHANNELS, PUBLISHER_PORT Проверка: python -c "from services.publisher.app.config import Settings; print(Settings().channels)" → ['@dedinit_vesti']
    • Замечание: токен не читался из-за порядка (os.getenv при ClassVar, до load_dotenv) и неверного пути BASE (3x parent → /opt/vesti/services, нужно parents[3] → /opt/vesti). Исправлено: Settings → @dataclass + post_init, load_dotenv(override=True), BASE=parents[3].

2. Telegram-клиент (Bot API через SOCKS5)

  • 2.1 telegram.py: клиент httpx с proxy=socks5://127.0.0.1:1080, методы sendMessage/sendPhoto/getMessage
  • 2.2 channels.py: разбор VESTI_BOT_CHANNELS ("@a,@b" → ["@a","@b"])
  • 2.3 Обработка ошибок: 403 (бот не админ), сеть (прокси) → 502

3. FastAPI-эндпоинты

  • 3.1 main.py: GET /healthz (status, bot, proxy, channels) Проверка: curl -s :8410/healthz → ok + бот + каналы. Реальное: {"status":"ok","bot":"dedinit_controller_bot","token_set":true,"proxy":"socks5://127.0.0.1:1080","channels":["@dedinit_vesti"]}
  • 3.2 main.py: POST /api/v1/publish (card {text, media?, direction, lang}, channels?) → results по каналам Проверка: curl -X POST :8410/api/v1/publish -d '{"card":{"text":"тест","direction":"linux","lang":"ru"}}' → ok, message_id в @dedinit_vesti. Реальное: {"ok":true,"results":{"@dedinit_vesti":{"message_id":2,...}}}
  • 3.3 views: эндпоинт GET /api/v1/views/{channel}/{mid} (в main.py) + поле views в ответе publish (get_views при публикации)

4. Контейнеризация

  • 4.1 Dockerfile: python:slim, non-root, read-only fs, expose 8410
  • 4.2 docker-compose.yml: сервис publisher, порт 127.0.0.1:8410, env из .env, healthcheck curl /healthz
    • Проверено 2026-09-09: docker compose up -d --build → vesti-publisher Up (healthy), 127.0.0.1:8410, healthz: bot=dedinit_controller_bot, proxy=host.docker.internal.
    • Питфолы Docker: (1) пути в compose отсчитываются от services/publisher/ → .env надо ../../.env, media ../../media; (2) TG_PROXY=127.0.0.1 в контейнере = сам контейнер → заменить на socks5://host.docker.internal:1080 + extra_hosts host-gateway; (3) load_dotenv(override=True) перебивал env контейнера → override=False (env окружения приоритетнее .env); (4) dataclass-дефолт tg_proxy="socks5://127.0.0.1:1080" был truthy → os.getenv не срабатывал → дефолт сделан пустым.

5. Интеграция с вебом

  • 5.1 web/app.py: убрать from publisher.bot import publish_multi/get_views_multi; вместо них web/publisher_client.py (HTTP POST на PUBLISHER_URL, default http://127.0.0.1:8410)
    • Проверка: grep — импорт publisher.bot убран; карточка шлёт card{direction,lang}, сервис берёт каналы из VESTI_BOT_CHANNELS.
  • 5.2 Обновить .env.example (VESTI_BOT_CHANNELS, TG_PROXY, PUBLISHER_URL)
  • 5.3 Реальный approve через веб: сквозной путь веб → HTTP → publisher(Docker) → канал.
    • Проверено 2026-09-09: approve постов 136 и 137 через POST /posts//approve — 302 → /published, статус published, бандл, tg_message_id=3 и 4 в @dedinit_vesti.
    • Веб-баги, найденные при проверке: (1) raise RedirectResponse(...) в _require_auth → TypeError (исключение не BaseException) → 500 на /candidates; фикс: HTTPException(303, headers={"Location": "/login"}); (2) tg_message_id для внешних постов искался по направлению (results["linux"]), а ключи results — каналы (@dedinit_vesti) → 0; фикс: брать первый message_id из results.values().

6. Проверка и документация

  • 6.1 openspec validate publisher-service → 0 ошибок (valid)
  • 6.2 Реальная публикация тестовой карточки в @dedinit_vesti (бот админ) → message_id в ответе. Реальное: message_id=2, ok.
    • Бот добавлен админом канала (подтверждено пользователем).
  • 6.3 Обновить STATUS.md / TODO.md — сделано; WALKTHROUGH/PRD — обновлено (см. PRD.md)

Открытые пункты

  • publisher: медиа из card.media — путь в БД /opt/vesti/media/... не совпадает с монтированием в контейнере (/srv/publisher/media) → send_photo не уходит при Docker-запуске (нужен маппинг путей или передача имени файла)