Source snapshot: 97d7f0fae96dc04b7ddad56fc1db6a108ed662cc [SEC-CLEAN] · pre-push-clean v1.0 · 109处敏感信息已自动转乱码
186 lines
6.0 KiB
Markdown
186 lines
6.0 KiB
Markdown
---
|
||
belongs_to:
|
||
- "[[INDEX · 铸渊·协作指令|GitHub ↔ Notion 桥接协议]]"
|
||
---
|
||
# 🔧 铸渊+舒舒指令|修复 Pipeline C 飞书广播推送 · 新增 /push-broadcast 路由(2026-03-15·霜砚签发)
|
||
|
||
<aside>
|
||
🚨
|
||
|
||
**紧急修复 · P0 · 霜砚签发 · 2026-03-15 02:00**
|
||
|
||
**前置已通**:Pipeline A(Notion 写入)✅ · Pipeline B(人格体唤醒+广播生成)✅
|
||
|
||
**本次修复**:Pipeline C(飞书广播推送)失败。服务器返回 HTTP 405 Not Allowed,原因是舒舒(gateway-server.js)上没有接收广播推送的路由。
|
||
|
||
**目标**:新增 `/push-broadcast` POST 路由,Pipeline C 推送广播内容到舒舒 → 舒舒调用飞书 API 发消息给目标用户 → 全链路闭环。
|
||
|
||
</aside>
|
||
|
||
---
|
||
|
||
## 一、错误日志(原文)
|
||
|
||
```
|
||
📢 推送广播到飞书...
|
||
标题: BC-M23-003-AW
|
||
目标: ou_3bd223b67374fd102edd4223244a7542
|
||
HTTP 响应: 405
|
||
<html>
|
||
<head><title>405 Not Allowed</title></head>
|
||
<body>
|
||
<center><h1>405 Not Allowed</h1></center>
|
||
<hr><center>nginx/1.24.0</center>
|
||
</body>
|
||
</html>
|
||
|
||
❌ 推送失败 (HTTP 405)
|
||
```
|
||
|
||
---
|
||
|
||
## 二、问题分析
|
||
|
||
Pipeline C 的 `push-broadcast-feishu.sh`(或类似脚本)向服务器发送 POST 请求推送广播,但:
|
||
|
||
1. **nginx** 没有配置该路由的 proxy_pass,直接返回 405
|
||
2. **舒舒(gateway-server.js)** 没有 `/push-broadcast` 路由来接收和处理广播推送
|
||
3. 即使有路由,还需要舒舒调用**飞书发消息 API** 把广播内容发给目标用户
|
||
|
||
---
|
||
|
||
## 三、修复指令
|
||
|
||
### 3.1 舒舒:新增 /push-broadcast POST 路由
|
||
|
||
**文件位置**:`feishu-bot/index.js`(或 `gateway-server.js`,取决于实际结构)
|
||
|
||
```jsx
|
||
// ============ 广播推送路由 ============
|
||
// Pipeline C 调用此路由,将广播推送到飞书
|
||
app.post('/push-broadcast', async (req, res) => {
|
||
try {
|
||
// 1. 验证 token
|
||
const token = req.headers['x-push-token'] || req.body.token;
|
||
if (token !== process.env.PUSH_BROADCAST_TOKEN) {
|
||
return res.status(401).json({ error: 'Invalid token' });
|
||
}
|
||
|
||
// 2. 解析请求体
|
||
const { title, content, target_open_id, chat_id } = req.body;
|
||
|
||
if (!content) {
|
||
return res.status(400).json({ error: 'Missing content' });
|
||
}
|
||
|
||
// 3. 获取飞书 tenant_access_token
|
||
const tokenRes = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
app_id: process.env.FEISHU_APP_ID,
|
||
app_secret: process.env.FEISHU_APP_SECRET
|
||
})
|
||
});
|
||
const tokenData = await tokenRes.json();
|
||
const accessToken = tokenData.tenant_access_token;
|
||
|
||
// 4. 发送飞书消息
|
||
// 优先发到群聊(chat_id),否则发给个人(target_open_id)
|
||
const receive_id_type = chat_id ? 'chat_id' : 'open_id';
|
||
const receive_id = chat_id || target_open_id;
|
||
|
||
const msgRes = await fetch(`https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=${receive_id_type}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${accessToken}`
|
||
},
|
||
body: JSON.stringify({
|
||
receive_id: receive_id,
|
||
msg_type: 'text',
|
||
content: JSON.stringify({ text: `📡 新广播 ${title || ''}\n\n${content}` })
|
||
})
|
||
});
|
||
const msgData = await msgRes.json();
|
||
|
||
if (msgData.code === 0) {
|
||
console.log(`✅ 广播推送成功: ${title} → ${receive_id}`);
|
||
res.json({ success: true, message_id: msgData.data?.message_id });
|
||
} else {
|
||
console.error(`❌ 飞书发送失败:`, msgData);
|
||
res.status(500).json({ error: msgData.msg, code: msgData.code });
|
||
}
|
||
|
||
} catch (err) {
|
||
console.error('❌ push-broadcast error:', err.message);
|
||
res.status(500).json({ error: err.message });
|
||
}
|
||
});
|
||
```
|
||
|
||
### 3.2 nginx:添加 proxy_pass(如果 /push-broadcast 没有被代理)
|
||
|
||
**文件位置**:`/etc/nginx/conf.d/guanghulab.conf`(或对应的 nginx 配置文件)
|
||
|
||
在 server block 中确认有如下配置:
|
||
|
||
```
|
||
location /push-broadcast {
|
||
proxy_pass http://127.0.0.1:3080; # 舒舒端口,按实际修改
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header Content-Type $content_type;
|
||
}
|
||
```
|
||
|
||
如果舒舒已经有一个通配 location 代理所有 API 请求(如 `location /api/`),则需要确认 `/push-broadcast` 路径在代理范围内,或者单独加一条。
|
||
|
||
### 3.3 Pipeline C 脚本:确认请求格式
|
||
|
||
Pipeline C 的推送脚本应该发送如下格式的 POST 请求:
|
||
|
||
```bash
|
||
curl -X POST https://guanghulab.com/push-broadcast \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-Push-Token: ${PUSH_BROADCAST_TOKEN}" \
|
||
-d '{
|
||
"title": "BC-M23-003-AW",
|
||
"content": "广播正文内容...",
|
||
"target_open_id": "ou_3bd223b67374fd102edd4223244a7542",
|
||
"chat_id": "(可选,群聊 ID)"
|
||
}'
|
||
```
|
||
|
||
---
|
||
|
||
## 四、环境变量确认
|
||
|
||
舒舒服务器上需要以下环境变量(`.env` 文件或 PM2 ecosystem):
|
||
|
||
| **变量名** | **用途** | **来源** |
|
||
| --- | --- | --- |
|
||
| `FEISHU_APP_ID` | 飞书应用 App ID | 飞书开放平台 → 应用凭证 |
|
||
| `FEISHU_APP_SECRET` | 飞书应用 App Secret | 飞书开放平台 → 应用凭证 |
|
||
| `PUSH_BROADCAST_TOKEN` | 推送鉴权 token | 与 GitHub Secret 中的 PUSH_BROADCAST_TOKEN 一致:`guanghu-push-2026` |
|
||
|
||
**注意**:`FEISHU_APP_ID` 和 `FEISHU_APP_SECRET` 舒舒发消息时就应该已经有了(舒舒接收飞书回调时用的同一个应用)。确认这两个变量在 `.env` 里存在即可。
|
||
|
||
---
|
||
|
||
## 五、验收标准
|
||
|
||
- [ ] 服务器上 `curl -X POST https://guanghulab.com/push-broadcast` 不再返回 405
|
||
- [ ] 带正确 token 和 content 的 POST 请求返回 `{"success": true}`
|
||
- [ ] 飞书端收到广播消息
|
||
- [ ] 完整链路测试:飞书发 SYSLOG → Pipeline A/B/C 全绿 → 飞书收到新广播
|
||
|
||
---
|
||
|
||
## 六、执行确认
|
||
|
||
铸渊完成后,在本页下方追加一行:
|
||
|
||
```
|
||
✅ push-broadcast 路由已部署 · [日期时间] · commit SHA
|
||
``` |