Files
wiki/tasks/multi-agent/DEV_TASK_WEBHOOKS.md
2026-05-21 16:00:01 +03:00

285 lines
10 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.
# DEV TASK: Настройка Webhooks (Plane + Gitea → Orchestrator)
**Статус:** Ready for dev
**Проект:** multi-agent
**Фаза:** 3
**Предыдущая задача:** DEV_TASK_ORCHESTRATOR_QG.md (выполнена)
---
## Цель
> Plane и Gitea автоматически отправляют webhook events в Orchestrator при действиях пользователя (создание задачи, комментарий, push, PR, CI status).
## Архитектура
Nginx proxy уже настроен: `https://openclaw.mva154.duckdns.org/orchestrator/``localhost:8500`.
Нужно:
1. Создать webhook в Gitea через API (events: push, pull_request, status)
2. Создать webhook в Plane через API или UI (events: work_item.created, comment.created)
3. Добавить HMAC-верификацию подписи в обоих handlers
4. Добавить webhook secret в .env Orchestrator'а
5. Проверить end-to-end: действие в UI → webhook → Orchestrator обрабатывает
## Стек / Зависимости
- httpx (уже есть)
- hmac + hashlib (stdlib)
- Gitea API v1
- Plane API v1
---
## Инфраструктура
| Параметр | Значение |
|----------|----------|
| Сервер | `slin@82.22.50.71` (mva154) |
| Orchestrator URL (external) | `https://openclaw.mva154.duckdns.org/orchestrator/` |
| Orchestrator URL (internal) | `http://127.0.0.1:8500/` |
| Gitea API | `http://localhost:3000/api/v1` |
| Gitea token | в .env `ORCH_GITEA_TOKEN` |
| Plane API | `http://localhost:8091/api/v1` |
| Plane token | в .env `ORCH_PLANE_API_TOKEN` |
| Plane workspace | `ag_proj` |
| Gitea repo owner | `admin` |
| Gitea repo | `enduro-trails` |
---
## Задачи
### Task 1: Создать webhook в Gitea через API
**Шаги:**
- [ ] **1.1** Создать webhook для репо `admin/enduro-trails`:
```bash
# Сгенерировать secret
GITEA_WEBHOOK_SECRET=$(openssl rand -hex 20)
echo "ORCH_GITEA_WEBHOOK_SECRET=${GITEA_WEBHOOK_SECRET}" >> /home/slin/repos/orchestrator/.env
# Создать webhook через API
curl -s -X POST "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \
-H "Authorization: token $(grep ORCH_GITEA_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" \
-H "Content-Type: application/json" \
-d '{
"type": "gitea",
"active": true,
"config": {
"url": "https://openclaw.mva154.duckdns.org/orchestrator/webhook/gitea",
"content_type": "json",
"secret": "'${GITEA_WEBHOOK_SECRET}'"
},
"events": ["push", "pull_request", "status"],
"branch_filter": "*"
}'
```
- [ ] **1.2** Проверить что webhook создан:
```bash
curl -s "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \
-H "Authorization: token $(grep ORCH_GITEA_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" | python3 -m json.tool
```
**Критерий готовности:** Webhook виден в Gitea UI (Settings → Webhooks), URL правильный.
---
### Task 2: Создать webhook в Plane
**Шаги:**
- [ ] **2.1** Проверить Plane API для webhooks:
```bash
# Plane webhooks API
curl -s "http://localhost:8091/api/v1/workspaces/ag_proj/webhooks/" \
-H "X-API-Key: $(grep ORCH_PLANE_API_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" | python3 -m json.tool
```
- [ ] **2.2** Создать webhook:
```bash
PLANE_WEBHOOK_SECRET=$(openssl rand -hex 20)
echo "ORCH_PLANE_WEBHOOK_SECRET=${PLANE_WEBHOOK_SECRET}" >> /home/slin/repos/orchestrator/.env
curl -s -X POST "http://localhost:8091/api/v1/workspaces/ag_proj/webhooks/" \
-H "X-API-Key: $(grep ORCH_PLANE_API_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane",
"is_active": true,
"secret_key": "'${PLANE_WEBHOOK_SECRET}'",
"project": null
}'
```
Примечание: Plane API для webhooks может отличаться от документации. Если API не работает — настроить через UI (`plane.mva154.duckdns.org` → Settings → Webhooks).
- [ ] **2.3** Если API не поддерживает webhooks — задокументировать и оставить TODO для ручной настройки через UI.
**Критерий готовности:** Webhook создан (через API или UI), URL указывает на Orchestrator.
---
### Task 3: HMAC-верификация подписи в webhook handlers
**Файлы:**
- Изменить: `src/webhooks/plane.py`
- Изменить: `src/webhooks/gitea.py`
- Изменить: `src/config.py`
**Шаги:**
- [ ] **3.1** Добавить верификацию в `src/webhooks/gitea.py`:
```python
import hmac
import hashlib
from ..config import settings
def verify_gitea_signature(body: bytes, signature: str) -> bool:
"""Verify Gitea webhook HMAC-SHA256 signature."""
if not settings.gitea_webhook_secret:
return True # Skip verification if no secret configured
expected = hmac.new(
settings.gitea_webhook_secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@router.post("/gitea")
async def gitea_webhook(request: Request):
body = await request.body()
signature = request.headers.get("X-Gitea-Signature", "")
if not verify_gitea_signature(body, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
# ... rest of handler
```
- [ ] **3.2** Добавить верификацию в `src/webhooks/plane.py`:
```python
# Plane использует свой формат подписи — проверить документацию
# Обычно: X-Plane-Signature header с HMAC-SHA256
def verify_plane_signature(body: bytes, signature: str) -> bool:
if not settings.plane_webhook_secret:
return True
expected = hmac.new(
settings.plane_webhook_secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
```
- [ ] **3.3** Добавить `from fastapi import HTTPException` в оба файла
**Критерий готовности:** Запрос без правильной подписи → 401. С правильной → 200.
---
### Task 4: Пересобрать и протестировать
**Шаги:**
- [ ] **4.1** Пересобрать Orchestrator:
```bash
cd /home/slin/repos/orchestrator && docker compose up -d --build
```
- [ ] **4.2** Проверить health:
```bash
curl -s http://localhost:8500/health
```
- [ ] **4.3** Тест Gitea webhook — сделать push в enduro-trails:
```bash
cd /home/slin/repos/enduro-trails
echo "# webhook test $(date)" >> .webhook-test
git add .webhook-test && git commit -m "test: webhook delivery check" && git push origin feature/ET-002-poi-toggle
```
- [ ] **4.4** Проверить что Orchestrator получил event:
```bash
docker logs orchestrator --tail 10 2>&1 | grep -i "gitea\|push"
```
- [ ] **4.5** Проверить в БД:
```bash
docker exec orchestrator python -c "
import sqlite3
conn = sqlite3.connect('/app/data/orchestrator.db')
rows = conn.execute('SELECT id, source, event_type, timestamp FROM events ORDER BY id DESC LIMIT 3').fetchall()
for r in rows: print(r)
"
```
- [ ] **4.6** Удалить тестовый файл:
```bash
cd /home/slin/repos/enduro-trails
git rm .webhook-test && git commit -m "test: cleanup webhook test" && git push origin feature/ET-002-poi-toggle
```
**Критерий готовности:** Push в Gitea → event появляется в Orchestrator БД. Логи показывают обработку.
---
### Task 5: Настроить Plane webhook через UI (если API не сработал)
Если Task 2 не удался через API:
- [ ] **5.1** Документировать инструкцию для ручной настройки:
```
1. Открыть https://plane.mva154.duckdns.org
2. Settings → Webhooks → Add Webhook
3. URL: https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane
4. Secret: <значение из ORCH_PLANE_WEBHOOK_SECRET в .env>
5. Events: All (или work_item.created, comment.created)
6. Save
```
- [ ] **5.2** Сохранить инструкцию в `/home/slin/repos/orchestrator/docs/SETUP_WEBHOOKS.md`
**Критерий готовности:** Документация есть. Webhook либо настроен, либо есть чёткая инструкция.
---
## Проверка (Acceptance)
| # | Проверка | Команда / Действие | Ожидаемый результат |
|---|----------|-------------------|---------------------|
| 1 | Gitea webhook создан | GET /api/v1/repos/admin/enduro-trails/hooks | Webhook с URL orchestrator |
| 2 | Push → event в БД | git push + check DB | Новый event source=gitea |
| 3 | Signature verification | curl без подписи → 401 | 401 Unauthorized |
| 4 | Signature verification | curl с правильной подписью → 200 | 200 accepted |
| 5 | Plane webhook | Создать issue в Plane → check DB | Event source=plane |
---
## Ограничения и контекст
- ⚠️ Plane API для webhooks может не поддерживать программное создание — тогда через UI
- ⚠️ Gitea webhook secret формат: `X-Gitea-Signature: sha256=<hex>`
- ⚠️ НЕ удалять существующие данные в БД
- ⚠️ Если signature verification ломает существующие smoke tests — сделать skip если secret пустой
- 🚫 НЕ менять Nginx конфиг (уже настроен)
- 🚫 НЕ менять порт Orchestrator'а
---
## Деплой-чеклист
- [ ] Webhook secret сгенерирован и добавлен в .env
- [ ] Gitea webhook создан через API
- [ ] Plane webhook создан (API или UI)
- [ ] HMAC verification добавлена в handlers
- [ ] `docker compose up -d --build` успешен
- [ ] Push в Gitea → event в Orchestrator
- [ ] Логи чистые, нет ошибок
---
*Создано: 2026-05-21 | Автор ТЗ: Стрим | Исполнитель: Dev-агент*