9.0 KiB
Gitea 到飞书 Webhook 中转服务
将 Gitea 的工单事件(创建、更新、关闭等)通过 Webhook 接收,转换为美观的飞书卡片消息,并转发到飞书群聊。
功能特性
- 接收 Gitea Webhook 事件(支持工单相关动作)
- 验证 Webhook 签名(可选)
- 将事件转换为飞书交互式卡片
- 支持自定义卡片标题、颜色、字段
- 完整的错误处理和日志记录
- 健康检查端点
- 易于部署和配置
系统架构
Gitea → Webhook → 中转服务 (Express) → 飞书机器人 → 飞书群聊
快速开始
前提条件
- Node.js 18+
- 一个飞书机器人,并获取其 Webhook URL
- Gitea 实例(版本 1.20+)
安装
- 克隆仓库
git clone <repository-url>
cd gitea-feishu-webhook-relay
- 安装依赖
npm install
- 配置环境变量
复制
.env.example为.env并填写实际值:
cp .env.example .env
编辑 .env:
PORT=3000
GITEA_WEBHOOK_SECRET=your_gitea_webhook_secret
FEISHU_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/xxx
LOG_LEVEL=info
运行
开发模式(使用 nodemon):
npm run dev
生产模式:
npm start
服务将在 http://localhost:3000 启动。
配置 Gitea Webhook
重要:选择正确的 Webhook 类型
确保在 Gitea 中创建 Webhook 时选择 “Gitea” 类型,而不是“飞书”或其他类型。Gitea 类型会发送结构化的 JSON 数据,便于解析。
详细字段填写(根据用户界面)
在 Gitea 的 Webhook 创建页面中,请按以下说明填写:
| 字段 | 填写示例 | 说明 |
|---|---|---|
| 目标 URL | http://192.168.0.15:3001 或 http://192.168.0.15:3001/webhook/gitea |
根据你的服务地址和端口(默认为3000,若修改了环境变量 PORT 则对应调整)。如果服务部署在本地,请使用内网 IP。 |
| HTTP 方法 | POST |
保持默认。 |
| 内容类型 | application/json |
保持默认。 |
| 授权标头 | (留空) | 除非你的服务需要 Bearer 或 Basic 认证,否则不需要填写。 |
| 密钥 | (可选)填写与 GITEA_WEBHOOK_SECRET 环境变量相同的字符串 |
用于验证 Webhook 请求的签名,增强安全性。如果留空,服务将跳过签名验证。 |
| 触发事件 | 勾选 “工单事件”(Issue) | 可根据需要勾选其他事件,但本服务目前只处理工单事件。 |
步骤
- 进入 Gitea 仓库的设置 → Webhooks → 添加 Webhook。
- 选择 “Gitea” 类型(不要选“飞书”或“自定义”)。
- 按照上表填写各字段。
- 点击“添加 Webhook”保存。
验证
保存后,Gitea 会立即发送一个测试事件(ping)。检查服务日志以确认接收成功。如果看到日志输出 Received Gitea webhook 且没有错误,说明配置正确。
飞书机器人配置
- 在飞书开放平台创建一个自定义机器人,获取 Webhook URL。
- 将机器人添加到目标群聊。
- 将 Webhook URL 填入
FEISHU_WEBHOOK_URL。
API 端点
GET /health– 健康检查POST /webhook/gitea– 接收 Gitea Webhook(推荐)POST /– 接收 Gitea Webhook(备用,兼容旧配置)
日志
日志使用 Winston 输出到控制台和文件:
logs/error.log– 错误日志logs/combined.log– 所有日志
自定义消息格式
如果你希望修改飞书卡片的样式、字段或映射关系,可以编辑 src/transformers/giteaToFeishu.js 文件。该文件导出了 transformToFeishuCard 函数,负责将 Gitea 事件转换为飞书卡片。
主要可配置部分
-
动作映射:
getCardInfo(action)函数定义了每个 Gitea 动作对应的卡片标题和颜色模板。你可以修改mapping对象来更改标题或颜色。 -
优先级提取:
extractPriority(labels)函数根据标签确定优先级。你可以调整逻辑以适应你的标签命名规范。 -
卡片元素:
transformToFeishuCard函数中的card.elements数组定义了卡片显示的字段。你可以添加、删除或修改元素来改变卡片内容。
标签颜色映射
服务支持根据工单标签自动调整卡片颜色。例如,如果工单带有 scope:非必要任务、scope:普通任务、scope:紧急任务、scope:重要任务 等标签,卡片头部颜色会相应变化:
| 标签 | 颜色 | 说明 |
|---|---|---|
scope:非必要任务 |
灰色 (grey) |
低优先级任务 |
scope:普通任务 |
蓝色 (blue) |
默认任务 |
scope:紧急任务 |
红色 (red) |
需要立即处理 |
scope:重要任务 |
橙色 (orange) |
重要但不紧急 |
颜色映射逻辑位于 getScopeColor(labels) 函数中。你可以修改该函数以支持更多标签或更改颜色。
此外,卡片会显示一个“范围”字段,展示匹配的标签名称。
示例:添加新字段
假设你想显示工单的创建时间,可以在 elements 数组中添加一个新的 div 元素:
{
tag: 'div',
text: {
tag: 'lark_md',
content: `**创建时间**:${new Date(issue.created_at).toLocaleString()}`
}
}
高级自定义
如果你需要更灵活的自定义(例如根据不同的仓库使用不同的模板),可以考虑以下方案:
- 创建新的转换器模块,并在
src/webhooks/gitea.js中替换导入。 - 使用配置文件(如
config/cardTemplates.json)来定义模板,并在转换器中读取。
注意事项
- 飞书卡片支持的元素和样式请参考飞书开放平台文档。
- 修改后请重启服务以使更改生效。
Docker 部署
提供 Dockerfile 便于容器化部署。
构建镜像:
docker build -t gitea-feishu-relay .
运行容器:
docker run -p 3000:3000 --env-file .env gitea-feishu-relay
环境变量
| 变量名 | 描述 | 默认值 |
|---|---|---|
| PORT | 服务监听端口 | 3000 |
| GITEA_WEBHOOK_SECRET | Gitea Webhook 签名密钥 | (空) |
| FEISHU_WEBHOOK_URL | 飞书机器人 Webhook URL | (必需) |
| LOG_LEVEL | 日志级别 (error, warn, info, debug) | info |
| NODE_ENV | 运行环境 (development, production) | development |
开发
项目结构
src/
├── server.js # Express 服务器入口
├── config.js # 配置管理
├── webhooks/
│ └── gitea.js # Webhook 处理器
├── transformers/
│ └── giteaToFeishu.js # 消息转换器
├── clients/
│ └── feishu.js # 飞书 API 客户端
└── utils/
└── logger.js # 日志工具
测试
运行单元测试:
npm test
测试建议
1. 启动服务
确保服务已运行并监听正确端口:
npm start
检查控制台输出是否有 Server listening on port 3001(或你设置的端口)。
2. 健康检查
使用 curl 或浏览器访问健康检查端点:
curl http://localhost:3001/health
应返回 {"status":"ok","timestamp":"..."}。
3. 模拟 Gitea Webhook 请求
你可以使用 curl 发送一个模拟的 Gitea 工单事件来测试转换和转发功能:
curl -X POST http://localhost:3001/webhook/gitea \
-H "Content-Type: application/json" \
-H "X-Gitea-Signature: sha256=..." \
-d '{
"action": "opened",
"issue": {
"id": 123,
"number": 45,
"title": "测试工单",
"body": "这是一个测试工单",
"state": "open",
"created_at": "2025-12-02T05:00:00Z",
"user": {
"id": 1,
"login": "testuser",
"full_name": "测试用户"
},
"assignee": null,
"labels": [],
"milestone": null
},
"repository": {
"id": 1,
"name": "test-repo",
"full_name": "org/test-repo",
"html_url": "https://gitea.example.com/org/test-repo"
},
"sender": {
"id": 1,
"login": "testuser"
}
}'
如果配置了 FEISHU_WEBHOOK_URL,服务会将转换后的卡片发送到飞书群聊。检查服务日志和飞书群聊以确认消息送达。
4. 查看日志
日志文件位于 logs/ 目录,或直接在控制台查看。关注 info 和 error 级别的消息。
故障排除
-
收不到飞书消息
- 检查
FEISHU_WEBHOOK_URL是否正确 - 查看日志中是否有错误信息
- 确认飞书机器人已加入群聊
- 检查
-
Gitea Webhook 验证失败
- 确保
GITEA_WEBHOOK_SECRET与 Gitea 中设置的密钥一致 - 检查请求头中是否包含
X-Gitea-Signature
- 确保
-
服务无法启动
- 确认端口未被占用
- 检查 Node.js 版本
-
Webhook 请求被忽略
- 确认 Gitea 发送的 JSON 格式符合预期(包含
action、issue、repository) - 检查日志中是否有
Event ignored消息,可能是动作被过滤(如labeled)
- 确认 Gitea 发送的 JSON 格式符合预期(包含
许可证
MIT