You've already forked GiteaToFeishuMsg
295 lines
9.0 KiB
Markdown
295 lines
9.0 KiB
Markdown
# Gitea 到飞书 Webhook 中转服务
|
||
|
||
将 Gitea 的工单事件(创建、更新、关闭等)通过 Webhook 接收,转换为美观的飞书卡片消息,并转发到飞书群聊。
|
||
|
||
## 功能特性
|
||
|
||
- 接收 Gitea Webhook 事件(支持工单相关动作)
|
||
- 验证 Webhook 签名(可选)
|
||
- 将事件转换为飞书交互式卡片
|
||
- 支持自定义卡片标题、颜色、字段
|
||
- 完整的错误处理和日志记录
|
||
- 健康检查端点
|
||
- 易于部署和配置
|
||
|
||
## 系统架构
|
||
|
||
```
|
||
Gitea → Webhook → 中转服务 (Express) → 飞书机器人 → 飞书群聊
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
### 前提条件
|
||
|
||
- Node.js 18+
|
||
- 一个飞书机器人,并获取其 Webhook URL
|
||
- Gitea 实例(版本 1.20+)
|
||
|
||
### 安装
|
||
|
||
1. 克隆仓库
|
||
```bash
|
||
git clone <repository-url>
|
||
cd gitea-feishu-webhook-relay
|
||
```
|
||
|
||
2. 安装依赖
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
3. 配置环境变量
|
||
复制 `.env.example` 为 `.env` 并填写实际值:
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
编辑 `.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):
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
生产模式:
|
||
```bash
|
||
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) | 可根据需要勾选其他事件,但本服务目前只处理工单事件。 |
|
||
|
||
### 步骤
|
||
1. 进入 Gitea 仓库的设置 → Webhooks → 添加 Webhook。
|
||
2. 选择 **“Gitea”** 类型(不要选“飞书”或“自定义”)。
|
||
3. 按照上表填写各字段。
|
||
4. 点击“添加 Webhook”保存。
|
||
|
||
### 验证
|
||
保存后,Gitea 会立即发送一个测试事件(ping)。检查服务日志以确认接收成功。如果看到日志输出 `Received Gitea webhook` 且没有错误,说明配置正确。
|
||
|
||
## 飞书机器人配置
|
||
|
||
1. 在飞书开放平台创建一个自定义机器人,获取 Webhook URL。
|
||
2. 将机器人添加到目标群聊。
|
||
3. 将 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 事件转换为飞书卡片。
|
||
|
||
### 主要可配置部分
|
||
|
||
1. **动作映射**:`getCardInfo(action)` 函数定义了每个 Gitea 动作对应的卡片标题和颜色模板。你可以修改 `mapping` 对象来更改标题或颜色。
|
||
|
||
2. **优先级提取**:`extractPriority(labels)` 函数根据标签确定优先级。你可以调整逻辑以适应你的标签命名规范。
|
||
|
||
3. **卡片元素**:`transformToFeishuCard` 函数中的 `card.elements` 数组定义了卡片显示的字段。你可以添加、删除或修改元素来改变卡片内容。
|
||
|
||
### 标签颜色映射
|
||
|
||
服务支持根据工单标签自动调整卡片颜色。例如,如果工单带有 `scope:非必要任务`、`scope:普通任务`、`scope:紧急任务`、`scope:重要任务` 等标签,卡片头部颜色会相应变化:
|
||
|
||
| 标签 | 颜色 | 说明 |
|
||
|------|------|------|
|
||
| `scope:非必要任务` | 灰色 (`grey`) | 低优先级任务 |
|
||
| `scope:普通任务` | 蓝色 (`blue`) | 默认任务 |
|
||
| `scope:紧急任务` | 红色 (`red`) | 需要立即处理 |
|
||
| `scope:重要任务` | 橙色 (`orange`) | 重要但不紧急 |
|
||
|
||
颜色映射逻辑位于 `getScopeColor(labels)` 函数中。你可以修改该函数以支持更多标签或更改颜色。
|
||
|
||
此外,卡片会显示一个“范围”字段,展示匹配的标签名称。
|
||
|
||
### 示例:添加新字段
|
||
|
||
假设你想显示工单的创建时间,可以在 `elements` 数组中添加一个新的 `div` 元素:
|
||
|
||
```javascript
|
||
{
|
||
tag: 'div',
|
||
text: {
|
||
tag: 'lark_md',
|
||
content: `**创建时间**:${new Date(issue.created_at).toLocaleString()}`
|
||
}
|
||
}
|
||
```
|
||
|
||
### 高级自定义
|
||
|
||
如果你需要更灵活的自定义(例如根据不同的仓库使用不同的模板),可以考虑以下方案:
|
||
|
||
- 创建新的转换器模块,并在 `src/webhooks/gitea.js` 中替换导入。
|
||
- 使用配置文件(如 `config/cardTemplates.json`)来定义模板,并在转换器中读取。
|
||
|
||
### 注意事项
|
||
|
||
- 飞书卡片支持的元素和样式请参考[飞书开放平台文档](https://open.feishu.cn/document/uAjLw4CM/ukzMukzMukzM/overview)。
|
||
- 修改后请重启服务以使更改生效。
|
||
|
||
## Docker 部署
|
||
|
||
提供 Dockerfile 便于容器化部署。
|
||
|
||
构建镜像:
|
||
```bash
|
||
docker build -t gitea-feishu-relay .
|
||
```
|
||
|
||
运行容器:
|
||
```bash
|
||
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 # 日志工具
|
||
```
|
||
|
||
### 测试
|
||
|
||
运行单元测试:
|
||
```bash
|
||
npm test
|
||
```
|
||
|
||
## 测试建议
|
||
|
||
### 1. 启动服务
|
||
确保服务已运行并监听正确端口:
|
||
```bash
|
||
npm start
|
||
```
|
||
检查控制台输出是否有 `Server listening on port 3001`(或你设置的端口)。
|
||
|
||
### 2. 健康检查
|
||
使用 curl 或浏览器访问健康检查端点:
|
||
```bash
|
||
curl http://localhost:3001/health
|
||
```
|
||
应返回 `{"status":"ok","timestamp":"..."}`。
|
||
|
||
### 3. 模拟 Gitea Webhook 请求
|
||
你可以使用 `curl` 发送一个模拟的 Gitea 工单事件来测试转换和转发功能:
|
||
|
||
```bash
|
||
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` 级别的消息。
|
||
|
||
## 故障排除
|
||
|
||
1. **收不到飞书消息**
|
||
- 检查 `FEISHU_WEBHOOK_URL` 是否正确
|
||
- 查看日志中是否有错误信息
|
||
- 确认飞书机器人已加入群聊
|
||
|
||
2. **Gitea Webhook 验证失败**
|
||
- 确保 `GITEA_WEBHOOK_SECRET` 与 Gitea 中设置的密钥一致
|
||
- 检查请求头中是否包含 `X-Gitea-Signature`
|
||
|
||
3. **服务无法启动**
|
||
- 确认端口未被占用
|
||
- 检查 Node.js 版本
|
||
|
||
4. **Webhook 请求被忽略**
|
||
- 确认 Gitea 发送的 JSON 格式符合预期(包含 `action`、`issue`、`repository`)
|
||
- 检查日志中是否有 `Event ignored` 消息,可能是动作被过滤(如 `labeled`)
|
||
|
||
## 许可证
|
||
|
||
MIT |