feat: add gitea/forgejo support

This commit is contained in:
RhenCloud 2026-08-11 22:01:04 +08:00
parent ace036c209
commit aec0d1a257
43 changed files with 683 additions and 130 deletions

View file

@ -13,7 +13,7 @@ https://your-worker.workers.dev
| 方法 | 路径 | 鉴权 | 说明 |
| -------- | ------------------------------ | ------------ | ------------------------------------------------ |
| `GET` | `/health` | 无 | 健康检查 |
| `POST` | `/webhook` | HMAC 签名 | GitHub webhook 接入 |
| `POST` | `/webhook` | HMAC 签名 | GitHub / Gitea webhook 接入(自动识别来源) |
| `POST` | `/discord/interactions` | Ed25519 签名 | Discord 交互斜杠命令、按钮、modal |
| `POST` | `/telegram/webhook` | Secret token | Telegram 更新bot `/gh` 命令) |
| `GET` | `/api/richheader` | 无 | 用于 Telegram 头像链接预览卡片的 Open Graph 页面 |

View file

@ -20,10 +20,13 @@ src/
├── server.ts # Hono 应用: /health、/webhook、/discord/interactions、/telegram/webhook挂载 /auth、/admin + /
├── core/
│ └── dispatch.ts # 平台中立分发:匹配路由 → formatEvent → getDriver().send/edit
├── events/ # GitHub webhook 事件流水线:验证签名、解析事件、匹配路由
│ ├── verify.ts # HMAC 签名验证 (Web Crypto时间安全)
│ ├── parse.ts # parseEvent (headers + body → WebhookEvent)
├── events/ # 与提供方无关的路由匹配
│ └── match.ts # matchRoute、eventOwners、extractBranch、关键词过滤
├── providers/ # Forge webhook 提供方(验证 + 解析/归一化)
│ ├── types.ts # Provider 接口matches/verify/parse
│ ├── index.ts # detectProvider() 注册表github、gitea
│ ├── github/ # X-GitHub-Event + X-Hub-Signature-256
│ └── gitea/ # X-Gitea-Event + X-Gitea-Signature归一化载荷
├── formatters/ # 平台中立格式化器(产出 NeutralMessage
│ ├── index.ts # formatEvent28 事件 switch → NeutralMessage + re-export
│ ├── colors.ts # GITHUB_COLORS + WORKFLOW_CONCLUSION_EMOJI

View file

@ -9,6 +9,7 @@ WebHooker 需要多个密钥才能运行。本地开发时存储在 `.dev.vars`
| 变量 | 说明 |
| ----------------------- | -------------------------------------------------------- |
| `GITHUB_WEBHOOK_SECRET` | GitHub App 设置中的 Webhook 密钥 |
| `GITEA_WEBHOOK_SECRET` | Gitea 实例的 Webhook 密钥(仅接收 Gitea webhook 时需要) |
| `GITHUB_CLIENT_ID` | App 设置中的 OAuth 客户端 ID |
| `GITHUB_CLIENT_SECRET` | App 设置中的 OAuth 客户端密钥 |
| `DISCORD_TOKEN` | Discord Bot Token |
@ -30,6 +31,17 @@ WebHooker 需要多个密钥才能运行。本地开发时存储在 `.dev.vars`
| `TELEGRAM_WEBHOOK_SECRET` | `POST /telegram/webhook` 验签密钥X-Telegram-Bot-Api-Secret-Token | 未设置时不校验 |
| `TELEGRAM_RICH_HEADER_HOST` | 外部 rich-header 服务的基础 URL未设置时使用内置的 `GET /api/richheader` 生成 Telegram 头像卡片 | 内置 `/api/richheader` |
## Webhook 提供方
WebHooker 通过同一个 `POST /webhook` 端点接收多个 forge 的 webhook按请求头自动识别来源只需把各 forge 的 webhook 指向 `{BASE_URL}/webhook` 即可。
| 提供方 | 事件请求头 | 签名请求头 | 签名格式 | 密钥 |
| ------ | ---------------- | --------------------- | -------------------------- | ----------------------- |
| GitHub | `X-GitHub-Event` | `X-Hub-Signature-256` | `sha256=<hex>` HMAC-SHA256 | `GITHUB_WEBHOOK_SECRET` |
| Gitea | `X-Gitea-Event` | `X-Gitea-Signature` | 纯 hex HMAC-SHA256 | `GITEA_WEBHOOK_SECRET` |
Gitea payload 会被归一化为与 GitHub 相同的内部结构,因此路由、过滤器与 28 个格式化器无需改动即可复用;未知或未映射的 Gitea 事件回退到通用格式化器。仓库/提交/用户链接基于 payload 的 `repository.html_url` 生成,会指向你的 Gitea 实例。
## Web 控制台
WebHooker 内置了位于 `/admin` 的配置控制台,可在浏览器中管理路由。它由 GitHub OAuth 和管理员白名单保护。
@ -156,17 +168,19 @@ WebHooker 内置了位于 `/admin` 的配置控制台,可在浏览器中管理
"id": "backend-team",
"name": "后端团队",
"adminIds": ["rhencloud"],
"owners": ["myorg"]
"owners": ["myorg"],
"providers": ["github", "gitea"]
}
```
| 字段 | 类型 | 必需 | 说明 |
| ---------- | -------- | ---- | ----------------------------------------------------- |
| `id` | string | 是 | 小写 id`a-z0-9``-`),由每条路由的 `groupId` 引用 |
| `name` | string | 是 | 可读的分组名称 |
| `adminIds` | string[] | 是 | 可管理该分组路由的 GitHub 用户 ID 或登录名 |
| `owners` | string[] | 否 | 允许事件进入该分组的组织/用户登录名;为空表示不限制 |
| `emoji` | boolean | 否 | 是否在该分组消息中显示 emoji默认 `true` |
| 字段 | 类型 | 必需 | 说明 |
| ----------- | -------- | ---- | ----------------------------------------------------------- |
| `id` | string | 是 | 小写 id`a-z0-9``-`),由每条路由的 `groupId` 引用 |
| `name` | string | 是 | 可读的分组名称 |
| `adminIds` | string[] | 是 | 可管理该分组路由的 GitHub 用户 ID 或登录名 |
| `owners` | string[] | 否 | 允许事件进入该分组的组织/用户登录名;为空表示不限制 |
| `providers` | string[] | 否 | 允许进入该分组的来源平台(`github``gitea`);为空表示全部 |
| `emoji` | boolean | 否 | 是否在该分组消息中显示 emoji默认 `true` |
### 权限模型
@ -174,6 +188,7 @@ WebHooker 内置了位于 `/admin` 的配置控制台,可在浏览器中管理
- **分组管理员**`adminIds`)只能查看和编辑其管理的分组;提交其分组之外的路由返回 `403`
- 分组管理端点通过 `/admin/api/groups/:id/routes` 一次只操作一个分组;`groupId` 由路径参数强制指定。
- `owners` 列表限定哪些事件参与者(发送者登录名)的事件会被该分组的路由投递。
- `providers` 列表限定哪个 forge`github``gitea`)的事件会被该分组的路由投递。即使组织/用户同名,也可以借此将 GitHub 与 Gitea 分组区分开。
## 过滤器类型

View file

@ -98,6 +98,14 @@ Worker 现在可通过 `https://webhooker.<your-subdomain>.workers.dev` 访问
2. 设置 **Webhook URL**`https://webhooker.<your-subdomain>.workers.dev/webhook`
3. 设置 **Webhook secret**`GITHUB_WEBHOOK_SECRET` 一致
### 6.(可选)配置 Gitea Webhook
1. 在 Gitea 仓库中进入 **设置 → Web 钩子 → 添加 Web 钩子 → Gitea**
2. 设置 **目标 URL**`https://webhooker.<your-subdomain>.workers.dev/webhook`
3. 设置 **HTTP 方法**`POST`、**Content Type** 为 `application/json`
4. 设置 **密钥**`GITEA_WEBHOOK_SECRET` 一致
5. 选择要触发的事件push、议题、拉取请求、发布等
## GitHub App 设置
### 1. 创建 App

View file

@ -1,12 +1,12 @@
# 简介
WebHooker 是一个基于 Cloudflare Workers 构建的 GitHub webhook 调度器。它接收 GitHub webhook 事件,应用可配置的过滤器,将事件格式化为富消息,并通过各自 REST API 投递到 Discord 频道/子区embed与 Telegram 群组/话题HTML。Discord 内的 `/gh` 交互通过 HTTPS Interactions EndpointEd25519 验签送达Telegram 的 `/gh` 命令通过 Telegram webhook 送达。路由与分组通过内置的 Web UI 管理。
WebHooker 是一个基于 Cloudflare Workers 构建的 GitHub/Gitea webhook 调度器。它接收来自受支持 forgeGitHub、Gitea——更多可通过 `src/providers/` 扩展)的 webhook 事件,应用可配置的过滤器,将事件格式化为富消息,并通过各自 REST API 投递到 Discord 频道/子区embed与 Telegram 群组/话题HTML。Discord 内的 `/gh` 交互通过 HTTPS Interactions EndpointEd25519 验签送达Telegram 的 `/gh` 命令通过 Telegram webhook 送达。路由与分组通过内置的 Web UI 管理。
## 架构
```text
GitHub Webhook → Cloudflare Worker (Hono)
├── POST /webhook → 验证 → 去重 → 过滤 → 格式化 → Discord (REST) / Telegram (Bot API)
GitHub / Gitea Webhook → Cloudflare Worker (Hono)
├── POST /webhook → 识别提供方 → 验证 → 去重 → 过滤 → 格式化 → Discord (REST) / Telegram (Bot API)
├── POST /discord/interactions → 验证 (Ed25519) → 处理 /gh 斜杠与右键命令
├── POST /telegram/webhook → 验证 (secret token) → 处理 /gh 命令
├── GET /auth/github → OAuth 流程
@ -27,10 +27,10 @@ GitHub Webhook → Cloudflare Worker (Hono)
### 数据流
1. GitHub 发送 webhook 到 `POST /webhook`
2. Worker 验证 HMAC-SHA256 签名
3. Worker 按 `X-GitHub-Delivery` 去重KV短 TTL丢弃重复投递
4. Worker 解析事件类型和载荷
1. 某个 forgeGitHub 或 Gitea发送 webhook 到 `POST /webhook`
2. Worker 根据请求头识别提供方(`X-GitHub-Event` / `X-Gitea-Event`)并验证对应提供的 HMAC-SHA256 签名
3. Worker 按投递 ID 去重KV短 TTL丢弃重复投递
4. Worker 解析事件类型并将载荷归一化为 GitHub 形状的事件
5. 根据过滤器event、repo、actor、action、branch、keyword与分组所有者限制评估路由
6. 匹配的路由触发格式化器函数生成平台中立消息
7. 每条消息通过 Discord 或 Telegram REST API 发送到对应路由的目标,并处理速率限制重试;`workflow_run` 进度原地更新。每次尝试都记录到 D1 发送日志

View file

@ -3,8 +3,8 @@ layout: home
hero:
name: WebHooker
text: GitHub Webhook → Discord
tagline: 通过 Cloudflare Workers 接收 GitHub 事件,应用过滤器,将格式化消息路由到 Discord 频道/子区与 Telegram 群组/话题。
text: GitHub / Gitea Webhook → Discord
tagline: 通过 Cloudflare Workers 接收 GitHub 与 Gitea 事件,应用过滤器,将格式化消息路由到 Discord 频道/子区与 Telegram 群组/话题。
actions:
- theme: brand
text: 快速开始