Telegram Bot 前端部署 500 错误排查记录
时间:2026-06-04 影响版本:菜单按项目分组重构后 状态:✅ 已解决
问题现象
菜单重构(按 Telegram / 云端存储 / 企业微信分组)推送后,访问 https://telegram.zhuonian.xyz 出现:
- 500 Internal Server Error(偶发)
- JS 文件 404(持续)——浏览器加载
index.html后,请求js/[hash].js返回 404
排查过程
第一轮:子菜单不显示
现象:Telegram 菜单下没有子菜单,设置菜单也是空的。
原因:路由文件中配置了 hideChildrenInMenu: true,这个配置的作用是完全隐藏子菜单,而不是折叠。
修复:移除所有路由文件中的 hideChildrenInMenu: true。
第二轮:网页 500 错误
现象:访问网页报 Internal Server Error。
排查:SSH 登录服务器,检查 /opt/telegram-bot/web/static/ 目录:
$ ls /opt/telegram-bot/web/static/
favicon.ico.gz index.html.gz发现两个问题:
favicon.ico缺失 —— 只有.gz压缩版,/favicon.ico路由返回 500index.html缺失 —— 只有index.html.gz,SPA fallback 找不到文件
修复(web/app.py):
python
# 修复前:favicon 不存在时抛出 500
@app.get("/favicon.ico")
async def favicon():
return FileResponse("web/static/favicon.ico") # 文件不存在 → 500
# 修复后:返回 204 No Content
@app.get("/favicon.ico")
async def favicon():
if os.path.exists("web/static/favicon.ico"):
return FileResponse("web/static/favicon.ico")
return Response(status_code=204)python
# 修复前:_serve_spa_index() 只找 index.html
def _serve_spa_index():
return FileResponse("web/static/index.html") # 不存在 → 500
# 修复后:优先 index.html,fallback 到 index.html.gz
def _serve_spa_index():
html_path = "web/static/index.html"
gz_path = html_path + ".gz"
if os.path.exists(html_path):
return FileResponse(html_path, media_type="text/html")
elif os.path.exists(gz_path):
return FileResponse(gz_path, media_type="text/html", headers={"Content-Encoding": "gzip"})
return Response("index.html not found", status_code=404)第三轮:JS 文件 404(根因)
现象:index.html 能访问了,但浏览器请求 js/[hash].js 返回 404。
排查:查看 GitHub Actions 构建日志(用户提供本地构建日志 C:\Users\zhuonian\Documents\3):
✓ 342 modules transformed.
dist/index.html 0.42 kB │ gzip: 0.29 kB
dist/assets/index-D9r7sFc2.css 34.61 kB │ gzip: 5.59 kB
dist/assets/index-D9r7sFc2.js chart.js:9447 Unknown option: .scale发现:CI 的 frontend-changed 检测逻辑有 bug:
yaml
# 旧的逻辑(有 bug)
- name: Check if frontend changed
run: |
git diff HEAD~1 HEAD --quiet -- frontend-new/ pnpm-lock.yaml || echo "frontend-changed=true" >> $GITHUB_ENVgit diff HEAD~1 HEAD 在以下情况下不可靠:
- 上次 commit 只改了后端 → 前端不会被构建 →
web/static/里的旧文件还在 - 但
index.html引用的js/[hash].js已经被新的 commit 更新了 → hash 不匹配 → 404
根因:CI 跳过了前端构建,web/static/ 里的 JS/CSS 文件版本与 index.html 中引用的 hash 不匹配。
解决方法
1. 重写 CI 配置(.github/workflows/deploy.yml)
去掉不可靠的 frontend-changed 检测,改为每次 push 都构建前端:
yaml
jobs:
build-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.2.2
- name: Setup Node.js
uses: setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: cd frontend-new && pnpm install --frozen-lockfile
- name: Build frontend
run: cd frontend-new && pnpm run build:antd
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: frontend-build
path: web/static/2. 改用 rsync 部署前端(避免垃圾文件积累)
旧方式(有风险):
yaml
# 先清空,再上传——两步之间如果失败,会导致文件不完整
- name: Deploy frontend to server
run: |
ssh ... "rm -rf /opt/telegram-bot/web/static/*"
scp -r frontend-build/* "...:/opt/telegram-bot/web/static/"新方式(原子性更强):
yaml
- name: Deploy frontend to server
run: |
rsync -avz --delete -e "ssh -i ~/.ssh/deploy_key -p $SSH_PORT -o StrictHostKeyChecking=no" \
frontend-build/ \
"$SSH_USER@$SSH_HOST:/opt/telegram-bot/web/static/"rsync --delete 会自动删除服务器端有、本地没有的文件,确保两端完全一致。
修复提交记录
| Commit | 说明 |
|---|---|
3788271 | 菜单按项目分组重构 |
f897e8f | 移除 hideChildrenInMenu |
d050ba6 | 从 git 索引移除 web/static(避免误提交构建产物) |
a61a802 | favicon.ico 不存在时返回 204 |
cb99a87 | app.py 正确处理 .gz 文件 |
| 最新 | 重写 deploy.yml,去掉 frontend-changed 检测 + 改用 rsync 部署 |
经验教训
- CI 条件判断要谨慎 ——
git diff HEAD~1 HEAD在 force push / squash merge 等场景下不可靠 - 前端构建产物不要进 Git —— 应该加入
.gitignore,由 CI 构建后直接部署 rsync --delete比rm + scp更安全 —— 原子性强,避免中间状态hideChildrenInMenu: true是完全隐藏子菜单,不是折叠,使用时要注意- 本地不要构建前端 —— 这是项目的铁律,所有构建都由 GitHub Actions 完成
预防措施
.github/workflows/deploy.yml已改为每次 push 都构建前端,不再依赖frontend-changed检测web/static/已加入.gitignore,避免误提交构建产物- 前端部署改用
rsync --delete,确保服务器端文件与构建产物完全一致 app.py增加了容错处理,favicon.ico和index.html不存在时返回 404/204 而非 500