Skip to content

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

发现两个问题

  1. favicon.ico 缺失 —— 只有 .gz 压缩版,/favicon.ico 路由返回 500
  2. index.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_ENV

git 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(避免误提交构建产物)
a61a802favicon.ico 不存在时返回 204
cb99a87app.py 正确处理 .gz 文件
最新重写 deploy.yml,去掉 frontend-changed 检测 + 改用 rsync 部署

经验教训

  1. CI 条件判断要谨慎 —— git diff HEAD~1 HEAD 在 force push / squash merge 等场景下不可靠
  2. 前端构建产物不要进 Git —— 应该加入 .gitignore,由 CI 构建后直接部署
  3. rsync --deleterm + scp 更安全 —— 原子性强,避免中间状态
  4. hideChildrenInMenu: true 是完全隐藏子菜单,不是折叠,使用时要注意
  5. 本地不要构建前端 —— 这是项目的铁律,所有构建都由 GitHub Actions 完成

预防措施

  1. .github/workflows/deploy.yml 已改为每次 push 都构建前端,不再依赖 frontend-changed 检测
  2. web/static/ 已加入 .gitignore,避免误提交构建产物
  3. 前端部署改用 rsync --delete,确保服务器端文件与构建产物完全一致
  4. app.py 增加了容错处理favicon.icoindex.html 不存在时返回 404/204 而非 500