# Webhook Setup: Plane + Gitea → Orchestrator ## Архитектура ``` Gitea (push/PR/CI) ──→ Nginx proxy ──→ Orchestrator /webhook/gitea Plane (work_item/comment) ──→ Nginx proxy ──→ Orchestrator /webhook/plane ``` External URL: `https://openclaw.mva154.duckdns.org/orchestrator/` Internal URL: `http://127.0.0.1:8500/` --- ## Gitea Webhook **Создан автоматически через API.** - URL: `https://openclaw.mva154.duckdns.org/orchestrator/webhook/gitea` - Events: `push`, `pull_request`, `status` - Secret: значение `ORCH_GITEA_WEBHOOK_SECRET` в `.env` - Signature header: `X-Gitea-Signature` (HMAC-SHA256 hex digest) ### Проверка ```bash GITEA_TOKEN=$(grep ORCH_GITEA_TOKEN /home/slin/repos/orchestrator/.env | cut -d= -f2) curl -s "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \ -H "Authorization: token ${GITEA_TOKEN}" | python3 -m json.tool ``` ### Пересоздание (если нужно) ```bash GITEA_WEBHOOK_SECRET=$(openssl rand -hex 20) # Обновить в .env: ORCH_GITEA_WEBHOOK_SECRET= curl -X POST "http://localhost:3000/api/v1/repos/admin/enduro-trails/hooks" \ -H "Authorization: token ${GITEA_TOKEN}" \ -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": "*" }' ``` --- ## Plane Webhook **Создан напрямую в PostgreSQL** (Plane CE не экспортирует webhook API через внешний /api/v1/). - URL: `https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane` - Events: `issue` (work_item.created), `issue_comment` (comment.created) - Secret: значение `ORCH_PLANE_WEBHOOK_SECRET` в `.env` - Signature header: `X-Plane-Signature` (HMAC-SHA256 hex digest) ### Проверка ```bash docker exec -e PGPASSWORD=plane plane-app-plane-db-1 psql -U plane -d plane -c \ "SELECT id, url, is_active FROM webhooks;" ``` ### Ручная настройка через UI (альтернатива) 1. Открыть `https://plane.mva154.duckdns.org` 2. Workspace Settings → Webhooks → Add Webhook 3. URL: `https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane` 4. Secret: значение из `ORCH_PLANE_WEBHOOK_SECRET` в `.env` 5. Events: Issue, Issue Comment 6. Save ### Пересоздание через SQL ```bash PLANE_WEBHOOK_SECRET=$(openssl rand -hex 20) # Обновить в .env: ORCH_PLANE_WEBHOOK_SECRET= WORKSPACE_ID=$(docker exec -e PGPASSWORD=plane plane-app-plane-db-1 psql -U plane -d plane -t -A -c \ "SELECT id FROM workspaces WHERE slug='ag_proj'") WEBHOOK_ID=$(cat /proc/sys/kernel/random/uuid) docker exec -e PGPASSWORD=plane plane-app-plane-db-1 psql -U plane -d plane -c " INSERT INTO webhooks (id, created_at, updated_at, deleted_at, workspace_id, url, is_active, secret_key, project, issue, module, cycle, issue_comment, is_internal, version) VALUES ('${WEBHOOK_ID}', NOW(), NOW(), NULL, '${WORKSPACE_ID}', 'https://openclaw.mva154.duckdns.org/orchestrator/webhook/plane', true, '${PLANE_WEBHOOK_SECRET}', true, true, false, false, true, false, 'v1'); " ``` --- ## HMAC Signature Verification Оба handler'а проверяют подпись: - Если secret пустой в `.env` — верификация пропускается (для dev/debug) - Если secret задан — запрос без валидной подписи получает `401 Unauthorized` ### Формат подписи | Source | Header | Algorithm | Format | |--------|--------|-----------|--------| | Gitea | `X-Gitea-Signature` | HMAC-SHA256 | hex digest (без префикса) | | Plane | `X-Plane-Signature` | HMAC-SHA256 | hex digest | ### Тест подписи вручную ```bash SECRET=$(grep ORCH_GITEA_WEBHOOK_SECRET /home/slin/repos/orchestrator/.env | cut -d= -f2) BODY='{"ref":"refs/heads/test","repository":{"name":"enduro-trails"},"commits":[]}' SIG=$(echo -n "${BODY}" | openssl dgst -sha256 -hmac "${SECRET}" | awk '{print $NF}') curl -X POST http://localhost:8500/webhook/gitea \ -H "Content-Type: application/json" \ -H "X-Gitea-Event: push" \ -H "X-Gitea-Signature: ${SIG}" \ -d "${BODY}" # Expected: {"status":"accepted"} ``` --- ## Переменные окружения (.env) | Переменная | Описание | |-----------|----------| | `ORCH_GITEA_WEBHOOK_SECRET` | HMAC secret для Gitea webhook | | `ORCH_PLANE_WEBHOOK_SECRET` | HMAC secret для Plane webhook | | `ORCH_GITEA_TOKEN` | API token для Gitea | | `ORCH_PLANE_API_TOKEN` | API token для Plane | --- ## Troubleshooting ```bash # Логи Orchestrator docker logs orchestrator --tail 50 2>&1 | grep -i "webhook\|signature\|401" # События в БД docker exec orchestrator python3 -c " import sqlite3 conn = sqlite3.connect('/app/data/orchestrator.db') for r in conn.execute('SELECT id, source, event_type, timestamp FROM events ORDER BY id DESC LIMIT 10').fetchall(): print(r) " # Gitea webhook delivery history # Gitea UI → Settings → Webhooks → click webhook → Recent Deliveries ``` --- *Создано: 2026-05-21 | Автор: Dev-агент*