openspec: архив 14 завершённых change-ов (веб-фиксы, crawler-queue, own-content-hub, publisher-service); спеки влиты в openspec/specs

This commit is contained in:
kpa39l
2026-09-16 17:01:00 +00:00
parent 584582a48c
commit 771f6a8276
88 changed files with 1632 additions and 3 deletions
@@ -0,0 +1,104 @@
# Design: publisher-service
## Approach
Выносим публикацию в Telegram из веб-процесса в изолированный FastAPI-микросервис.
Сервис — единственная точка, которая знает токен бота (прокси), каналы и Bot API.
Остальные компоненты (веб, в будущем cron/боты) вызывают его по HTTP.
Схема:
```
vesti-web (:8400) ──POST /api/v1/publish──▶ publisher-service (:8410) ──Bot API (SOCKS5 127.0.0.1:1080)──▶ Telegram
approve (человек) │ VESTI_BOT_TOKEN, VESTI_BOT_CHANNELS
▼
Telegram: @dedinit_vesti (+ другие каналы)
```
## Files
```bash
# Новый сервис
services/publisher/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI: POST /api/v1/publish, GET /healthz
│ ├── config.py # env: VESTI_BOT_TOKEN, TG_PROXY, VESTI_BOT_CHANNELS, LISTEN_PORT
│ ├── telegram.py # Bot API клиент (httpx + SOCKS5): send, get_views
│ └── channels.py # разбор списка каналов (VESTI_BOT_CHANNELS)
├── requirements.txt # fastapi, uvicorn, httpx[socks], pydantic, python-dotenv
├── Dockerfile # python:slim, non-root, read-only fs
├── docker-compose.yml # сервис, порт 8410, healthcheck, env из .env
├── .env.example # без секретов
└── README.md # API, порты, запуск, безопасность
# Изменения
web/app.py # approve → HTTP POST в publisher-service (вместо import publisher.bot)
.env.example # + VESTI_BOT_CHANNELS, TG_PROXY
STATUS.md / TODO.md / WALKTHROUGH.md # статус
```
## Data / Config
```bash
# .env (реальные значения; НЕ коммитить)
VESTI_BOT_TOKEN=<токен @dedinit_controller_bot> # уже в .env
TG_PROXY=socks5://127.0.0.1:1080 # уже есть
VESTI_BOT_CHANNELS=@dedinit_vesti # список каналов, разделитель запятая
PUBLISHER_PORT=8410
```
## API
### POST /api/v1/publish
```json
{
"card": {"text": "...", "media": "/path/to/photo.jpg", "direction": "linux", "lang": "ru"},
"channels": ["@dedinit_vesti"] // опционально; default = VESTI_BOT_CHANNELS
}
```
Ответ 200:
```json
{
"ok": true,
"results": {
"@dedinit_vesti": {"message_id": 123, "media_message_id": 122, "views": 0}
},
"dry_run": false
}
```
Ошибки: 400 (невалидный card), 502 (Bot API / прокси недоступен), 403 (бот не админ).
### GET /healthz
```json
{"status": "ok", "bot": "@dedinit_controller_bot", "proxy": "socks5://127.0.0.1:1080", "channels": ["@dedinit_vesti"]}
```
Healthcheck: `curl -f http://127.0.0.1:8410/healthz`.
## Commands
```bash
cd /opt/vesti
# dev (без Docker):
.venv/bin/pip install -r services/publisher/requirements.txt
.venv/bin/uvicorn services.publisher.app.main:app --host 127.0.0.1 --port 8410
curl -s http://127.0.0.1:8410/healthz
# prod (Docker):
docker compose -f services/publisher/docker-compose.yml up -d --build
curl -s http://127.0.0.1:8410/healthz
curl -s -X POST http://127.0.0.1:8410/api/v1/publish \
-H 'Content-Type: application/json' \
-d '{"card": {"text": "<b>Тест</b>", "direction": "linux", "lang": "ru"}}'
# переключение веба:
# в web/app.py заменить `from publisher.bot import publish_multi` на HTTP-клиент
# (или переменная окружения PUBLISHER_URL=http://127.0.0.1:8410)
```
## Verification
- [ ] `openspec validate publisher-service` → 0 ошибок
- [ ] `curl :8410/healthz` → ok, бот, прокси, каналы
- [ ] POST /api/v1/publish с тестовой карточкой → message_id в @dedinit_vesti
- [ ] Бот не админ / прокси упал → понятная ошибка (4xx/502), веб показывает
- [ ] approve в вебе → реальное сообщение в канале (микросервис вызван по HTTP)