feat: per-group webhook ingress, custom webhooks, GitHub App tenant isolation

Add POST /webhook/{groupId} with per-group secrets (KV tenant:{groupId}), a custom provider (X-WebHooker-Signature HMAC, arbitrary JSON -> custom events through the route pipeline), and GitHub App installation isolation (Group.installationId) with automatic provisioning on installation.created (inst-{id} groups or binding matching owners groups). Includes WebhookPanel admin UI, custom route template, docs and 157 passing tests.
This commit is contained in:
RhenCloud 2026-08-13 09:24:50 +08:00
parent 0b078d938b
commit b600f02027
No known key found for this signature in database
GPG key ID: A574A617378C4E0B
34 changed files with 1711 additions and 183 deletions

View file

@ -10,30 +10,31 @@ https://your-worker.workers.dev
## Endpoints
| Method | Path | Auth | Description |
| -------- | ------------------------------ | ----------------- | --------------------------------------------------------- |
| `GET` | `/health` | None | Health check |
| `POST` | `/webhook` | HMAC signature | GitHub / Gitea webhook ingestion (provider auto-detected) |
| `POST` | `/discord/interactions` | Ed25519 signature | Discord interactions (slash commands, buttons, modals) |
| `POST` | `/telegram/webhook` | Secret token | Telegram updates (bot `/gh` commands) |
| `GET` | `/api/richheader` | None | Open Graph page for the Telegram avatar link-preview card |
| `GET` | `/auth/github` | None | Start GitHub OAuth flow |
| `GET` | `/auth/github/callback` | None | OAuth callback |
| `DELETE` | `/auth/token/:userId` | None | Revoke user token |
| `POST` | `/api/comment` | Bearer token | Create issue comment |
| `POST` | `/api/merge` | Bearer token | Merge pull request |
| `POST` | `/api/close` | Bearer token | Close pull request |
| `POST` | `/api/react` | Bearer token | Add reaction to issue |
| `GET` | `/admin` | Admin session | Config console UI |
| `GET` | `/admin/api/routes` | Admin session | List routes |
| `PUT` | `/admin/api/routes` | Admin session | Replace routes |
| `GET` | `/admin/api/groups` | Admin session | List groups (scoped) |
| `PUT` | `/admin/api/groups` | Admin session | Replace groups (super) |
| `GET` | `/admin/api/groups/:id/routes` | Admin session | List a group's routes |
| `PUT` | `/admin/api/groups/:id/routes` | Admin session | Replace a group's routes |
| `GET` | `/admin/api/me` | Admin session | Current session info |
| `GET` | `/admin/api/logs` | Admin session | Send logs (scoped) |
| `GET` | `/admin/api/logs/:id` | Admin session | Single send-log entry (scoped) |
| Method | Path | Auth | Description |
| -------- | ------------------------------ | ----------------- | ------------------------------------------------------------------ |
| `GET` | `/health` | None | Health check |
| `POST` | `/webhook` | HMAC signature | GitHub / Gitea / custom webhook ingestion (provider auto-detected) |
| `POST` | `/webhook/:groupId` | Per-group secret | Per-group webhook ingress (only that group's routes fire) |
| `POST` | `/discord/interactions` | Ed25519 signature | Discord interactions (slash commands, buttons, modals) |
| `POST` | `/telegram/webhook` | Secret token | Telegram updates (bot `/gh` commands) |
| `GET` | `/api/richheader` | None | Open Graph page for the Telegram avatar link-preview card |
| `GET` | `/auth/github` | None | Start GitHub OAuth flow |
| `GET` | `/auth/github/callback` | None | OAuth callback |
| `DELETE` | `/auth/token/:userId` | None | Revoke user token |
| `POST` | `/api/comment` | Bearer token | Create issue comment |
| `POST` | `/api/merge` | Bearer token | Merge pull request |
| `POST` | `/api/close` | Bearer token | Close pull request |
| `POST` | `/api/react` | Bearer token | Add reaction to issue |
| `GET` | `/admin` | Admin session | Config console UI |
| `GET` | `/admin/api/routes` | Admin session | List routes |
| `PUT` | `/admin/api/routes` | Admin session | Replace routes |
| `GET` | `/admin/api/groups` | Admin session | List groups (scoped) |
| `PUT` | `/admin/api/groups` | Admin session | Replace groups (super) |
| `GET` | `/admin/api/groups/:id/routes` | Admin session | List a group's routes |
| `PUT` | `/admin/api/groups/:id/routes` | Admin session | Replace a group's routes |
| `GET` | `/admin/api/me` | Admin session | Current session info |
| `GET` | `/admin/api/logs` | Admin session | Send logs (scoped) |
| `GET` | `/admin/api/logs/:id` | Admin session | Single send-log entry (scoped) |
## Admin Console
@ -93,6 +94,18 @@ When `X-GitHub-Delivery` is present and the same delivery was already processed
| `400` | `{"error": "Invalid event"}` | Missing event header or malformed body |
| `413` | `{"error": "Request too large"}` | Body exceeds 1MB limit |
### Per-Group Webhook (`POST /webhook/:groupId`)
Verifies the payload against the **group's** secret (KV `tenant:{groupId}`, generated from the console — Webhook endpoint panel) instead of the global secrets, and dispatches only into that group's routes. Works for GitHub (`X-Hub-Signature-256`), Gitea (`X-Gitea-Signature`) and custom (`X-WebHooker-Signature`) senders. Returns `404` when the group does not exist or has no secret configured.
### Custom Webhooks
Any JSON payload signed with `X-WebHooker-Signature: sha256=<hex>` (HMAC-SHA256 of the raw body, group or global secret) becomes a `custom` event. Route it with a route whose filter is `event: custom`. Payload schema: see [Configuration → Custom webhooks](../guide/configuration.md#custom-webhooks).
### GitHub App Installation Events
`installation` webhook events (`created`, ...) are auto-provisioned: a group named after the installing account (`inst-{installationId}`, bound via `installationId`) is created automatically, or existing groups whose `owners` match the installing account are bound to the installation. See [Configuration → GitHub App tenant isolation](../guide/configuration.md#github-app-tenant-isolation).
## Error Format
All error responses follow the format: