把每日科技日报改成服务器自运行

Date 2026-05-26 · Category blog · Status finished · Confidence likely
工程实践, Web 开发

背景

这次调整不是为了“换一种自动化工具”,而是为了解决两个实际问题:

  • 当前机器访问 GitHub 不稳定,git push 和 GitHub Actions 触发链路经常中断。
  • 日报如果只在远端生成,但我后续继续从本地上传 dist,服务器上新增的新闻内容又可能被下一次手动发布覆盖掉。

原来的设计里,日报生成和站点部署都依赖 GitHub。理论上流程清晰,但在网络不稳定时,最先失效的恰好就是自动发布链。

所以这次没有继续在 GitHub 侧修补,而是把整条链路收回到服务器本机:抓取新闻、生成 Markdown、更新 RSS / sitemap、构建站点、原子发布,全部在服务器侧完成。

为什么改成服务器自运行

目标其实很简单:

  1. 日报每天能稳定跑。
  2. 手动发版和自动日报走同一条发布链。
  3. 自动生成的内容不会被后续部署覆盖。

这意味着不能只改“定时任务在哪里触发”,还要一起改“站点最终以谁为准”。

如果只是把 GitHub Actions 换成别的 CI,而站点仍然靠本地上传 dist,问题并没有真正解决。因为构建时读取的仍然是本地源码,而不是服务器上已经生成过的日报 Markdown。

所以这次的核心决定其实是两条:

  • 每日新闻任务改为服务器定时执行。
  • 手动发布也改成“同步源码到服务器,再由服务器本机构建并上线”。

调整后的链路

现在的发布链是这样的:

本地修改源码
  ↓
同步源码到 /opt/techloop-blog
  ↓
服务器保留 ai-dev-daily-*.md
  ↓
服务器本机 build
  ↓
原子切换 /var/www/blog

日报任务则独立跑在同一份服务器源码上:

08:10 定时触发
  ↓
抓取 RSS
  ↓
筛选前一自然日新闻
  ↓
调用 OpenAI 整理中文日报
  ↓
写入 src/posts/ai-dev-daily-YYYY-MM-DD.md
  ↓
生成 RSS / sitemap
  ↓
构建并发布

这样做有一个直接结果:自动日报和手动发版不再是两套互相覆盖的来源,而是统一到同一份服务器工作目录。

这次实际补了什么

这次没有改前端展示逻辑,首页、动态页和文章页仍然沿用现在的 category: news 内容入口。真正变的是自动化层。

主要新增了三类脚本:

  • 服务器日报入口:负责生成日报、识别 skip、写日志。
  • 服务器构建发布入口:负责 npm run build 和原子切站。
  • systemd 定时任务:负责每天 08:10 Asia/Shanghai 触发。

同时本地发布脚本也改了逻辑:

  • 以前是本地 build 后直接上传 dist
  • 现在是把源码同步到服务器,再由服务器本机构建上线。

这个调整看起来比“只改一个 cron”大一些,但它解决的是整条链路的一致性问题。

运行结果

这次上线后,已经手动验证过两个场景。

第一种是指定历史日期回放:

scripts/deploy/server-daily-news.sh --date 2026-05-22

它成功生成了:

src/posts/ai-dev-daily-2026-05-22.md

并完成了构建与上线。

第二种是直接触发当天默认任务。系统会去处理前一个自然日的内容,如果最终唯一新闻不足 3 条,就正常跳过:

skip: only 1 unique items for 2026-05-25

这里的 skip 不是错误,而是当前策略本身的一部分:宁缺毋滥,不为凑数发一篇质量过低的日报。

过程里遇到的两个问题

1. OpenAI 中转站只支持 chat completions

这次日报生成用的是 OpenAI 兼容接口,但实际接入的是一个中转站。它只支持 /v1/chat/completions,而且在某些情况下还要求 stream: true

所以日报脚本在调用层做了兼容:

  • 支持自定义 OPENAI_BASE_URL
  • 改用 chat completions
  • 中转站要求流式时自动回退到流式请求

这样才让脚本能在当前环境里稳定跑通。

2. 服务器 Node 版本过低

服务器原来是 Node 18,构建虽然勉强能跑,但依赖已经提示 react-router 要求 >=20

最后还是把服务器升级到了 Node 20,再重新验证了日报脚本和 systemd 触发链。这个问题如果不提前处理,后面自动任务会存在延迟爆雷的风险。

现在的状态

目前这条链路已经满足最初目标:

  • 日报不再依赖 GitHub Actions。
  • 自动日报和手动发版走同一条服务器发布链。
  • 自动生成的新闻内容不会再被下一次本地部署覆盖。
  • 少于 3 条新闻时任务成功跳过,不会生成凑数稿。

从维护角度看,这比继续折腾 GitHub 网络问题更直接。发布链越靠近真正提供服务的那台机器,越容易控制,也越容易排查。

后续还可以继续优化什么

现在这版已经能用,但还可以继续往前走两步:

  • 扩大 RSS 来源,减少“当天不足 3 条”的情况。
  • 增加一个回退策略,例如目标日期新闻不足时,再补看近 48 小时窗口。
  • 给自动生成的日报加更明确的内部标识,便于后台或后续统计处理。
  • 再补一层监控,例如任务失败时发一条简单通知。

不过这些都属于下一阶段。对当前博客来说,更重要的是先把“自动生成并稳定上线”这件事做扎实。


See also