10 KiB
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.
Нужно:
- Создать webhook в Gitea через API (events: push, pull_request, status)
- Создать webhook в Plane через API или UI (events: work_item.created, comment.created)
- Добавить HMAC-верификацию подписи в обоих handlers
- Добавить webhook secret в .env Orchestrator'а
- Проверить 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:
# Сгенерировать 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 создан:
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:
# 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:
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:
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:
# 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:
cd /home/slin/repos/orchestrator && docker compose up -d --build
- 4.2 Проверить health:
curl -s http://localhost:8500/health
- 4.3 Тест Gitea webhook — сделать push в enduro-trails:
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:
docker logs orchestrator --tail 10 2>&1 | grep -i "gitea\|push"
- 4.5 Проверить в БД:
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 Удалить тестовый файл:
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-агент