# Gitea 到飞书 Webhook 中转服务 将 Gitea 的工单事件(创建、更新、关闭等)通过 Webhook 接收,转换为美观的飞书卡片消息,并转发到飞书群聊。 ## 功能特性 - 接收 Gitea Webhook 事件(支持工单相关动作) - 验证 Webhook 签名(可选) - 将事件转换为飞书交互式卡片 - 支持自定义卡片标题、颜色、字段 - 完整的错误处理和日志记录 - 健康检查端点 - 易于部署和配置 ## 系统架构 ``` Gitea → Webhook → 中转服务 (Express) → 飞书机器人 → 飞书群聊 ``` ## 快速开始 ### 前提条件 - Node.js 18+ - 一个飞书机器人,并获取其 Webhook URL - Gitea 实例(版本 1.20+) ### 安装 1. 克隆仓库 ```bash git clone 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