164 lines
5.3 KiB
Markdown
164 lines
5.3 KiB
Markdown
# 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=<new_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=<new_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-агент*
|