上线和部署不是一回事。 GitHub Pages 很早就通了:推送、构建、出页面,一条流水线跑得很顺。但那只是「能构建」,离「能写」还差一步——我想在手机上打开一个网页就能改文章。
问题出在登录。用 GitHub 账号登录后台,需要拿授权码去换一枚访问令牌,而换令牌这一步必须由服务端来做:把 client secret 放在浏览器里等于公开它。GitHub Pages 是纯静态托管,跑不了任何服务端代码,所以这一步它做不了。于是同一份源码又接了一个 Cloudflare Pages:静态页面照旧,另外两个函数负责 OAuth 的两跳。两个平台连同一个仓库,内容推一次,两边各自重新部署。
一个只有真实浏览器才暴露的 bug。 回调页要把令牌送回后台,消息体本该是 JSON 字符串,当时却写成了对象字面量。JavaScript 里用 + 拼接字符串和对象会得到 [object Object],后台拿它去 JSON.parse,直接抛异常——登录按钮点下去,弹窗正常打开、授权正常完成、然后什么都没有发生。
这种 bug 很难断点调试,因为它在代码层面看起来完全正常:函数返回 200、页面渲染成功、日志没有报错。只有把真实客户端、真实弹窗、真实握手全跑一遍才会露出来。
三个被实测推翻的假设:
浏览器路由能拦住一切请求——实际上,服务端 302 跳转之后的下一跳,拦截器看不到。
函数里的 fetch 会经过浏览器——不会,它跑在服务端进程里,浏览器层的替身完全无效。
弹窗回到自己页面 window.opener 就还在——只要中途加载过 GitHub 登录页,那个响应的 COOP 头就会切断弹窗和后台的关系,之后再怎么导航回来都握不上手。
验收标准:真的提交进仓库。「能登录」只是表象。我在线上后台新建了一篇文章,仓库里出现一条提交;再从后台删掉,仓库里又出现一条提交,文件消失,GitHub Pages 那边那个地址跟着从 200 变成 404。两个平台都在推送后自动重建,这条链路才算立住。
一句话总结: 静态博客不需要服务器,但「能在网页里写作」这件事,仍然需要一个很小的服务端函数来守住那枚令牌。二十行代码,值得。
