Hexo 博客部署到 Cloudflare Workers

Hexo 博客部署到 Cloudflare Workers

Cloudflare Pages 影响了我博客的评论系统

为了借助 Cloudflare Pages 实现全球加速和持续集成/持续部署(CI/CD),我的 博客 (以下简称 本博)一开始有一个镜像是部署在 Cloudflare Pages [1]

但是,我后来发现 Cloudflare Pages 会自动移除 URL 末尾的扩展名,例如访问 https://example.tld/file123.html,它就会 302 跳转到 https://example.tld/file123。官方称之为 Route matching(路由匹配)[2]Route matching 无法手动关闭

Route matching

这个功能影响了 本博 评论的加载。本博 的评论系统是 Valine,它是根据静态页面的文件名来储存评论数据的,比方说,它会把 https://example.tld/file123.html 这个页面的所有评论放在数据库的 file123.html 表中。所以 本博 不同镜像的同一篇文章会共享同样的评论。但是 Route matching 去掉了 .html 后缀,导致 Valine 评论系统会在数据库新建一个 file123 表,从而使得部署在 Cloudflare Pages 上的 本博镜像 不能与其他的镜像共享评论。

但是我又不想放弃 Cloudflare,毕竟它的节点遍布全球(中国大陆除外),真的能够加速全球访问啊。

两种可行的解决方案

我想出两种解决方案:

  1. 放弃 Cloudflare Pages,而是部署到 Cloudflare Workers,后者也是利用 Cloudflare 的 CDN 加速全球访问。

  2. 二是修改 本博_config.yml,去掉每个静态页面的 URL 的 .html 扩展名,然后配置 301 跳转。

尽管两个方案都挺麻烦,但是我倾向于用第一个,因为改 URL 和配置跳转都会影响搜索引擎对 本博 的收录。

部署到 Cloudflare Workers

前人[3][4][5]已经写得非常明确了,此处结合 本博 的实际作了补充,并且详细解读配置文件的编写。

创建 Cloudflare API 令牌

打开 Cloudflare 的 API 令牌申请页面 ,在此申请一个 API 令牌(API token)。

点击“创建令牌”。

找到“编辑 Cloudflare Workers”,点击右侧的“使用模板”。

编辑 Cloudflare Workers

去掉“账户设置”和“用户详细信息”权限。

去掉“账户设置”和“用户详细信息”权限。

“账户资源”和“区域资源”按照权限最小化的原则设置。然后点击“继续以显示摘要”。

“账户资源”和“区域资源”

最后,点击“创建令牌”,完成。

点击“创建令牌”

将生成的令牌(一个字符串)保存在安全的地方,这个令牌只显示一次,下次不再显示。

安装 Wrangler

以下以 PowerShell 为例说明命令。假设我的博客源码在 C:\Users\Kukmoon\Kukmoon_blog 文件夹。

1
2
cd ~\kukmoon_blog
npm i @cloudflare/wrangler -g

初始化项目

1
2
cd ~\kukmoon_blog
wrangler init --site blog

Cloudflare Workers 会为项目分配一个四级域名。上述命令中的 blog 是项目的名称,也是域名的一部分,读者可以换成自己喜欢的名称。我在 Cloudflare 的用户名是 kukmoon,那么 本博 部署到 Cloudflare Workers 以后,URL 就是 https://blog.kukmoon.workers.dev

执行以上命令,会在我的博客源码文件夹中生成配置文件 wrangler.toml 和几个文件夹。下一步需要修改配置文件。

配置 wrangler.toml

用文本编辑器打开 wrangler.toml

1
2
3
4
5
6
7
8
9
10
name = "blog"
type = "webpack"
account_id = ""
route = 'blog2.kukmoon.com/*'
zone_id = ''
usage_model = ''
compatibility_flags = []
workers_dev = true
site = {bucket = "./public",entry-point = "workers-site"}
compatibility_date = "2022-04-07"

详细解读:

  • name:上一步的命令 wrangler init --site blog 中的 blog

  • account_id:我的 Cloudflare 账户 ID。在 Cloudflare 的面板 中,点开绑定到 Cloudflare 的某个域名,在右下角“API”域名中查找“账户 ID”。
    账户 ID

  • route:自定义域名。注意:一定不能忽略末尾的 /*,这是由 Cloudflare 的匹配规则决定的。

  • zone_id:网站的区域 ID。在 Cloudflare 的面板 中,点开绑定到 Cloudflare 的某个域名,在右下角“API”域名中查找“区域 ID”。
    区域 ID

  • workers_dev:是否启用 Cloudflare Workers 为项目分配的四级域名,此处为 https://blog.kukmoon.workers.dev 设置为 true 即可。

  • site:要把 bucket 设为 ./public,即 Hexo 渲染生成的静态页面所在的文件夹。

配置 API 令牌

保存以上文件后,运行 wrangler config 按照提示输入刚刚获取到的 API 令牌。
配置 API 令牌

预览和发布

在博客源码所在的文件夹下执行以下命令。

生成静态文件

1
hexo g

预览。运行下述命令可以预览,这会将 bucket 中的文件上传到 Cloudflare Workers KV (Cloudflare Workers 的对象存储空间)中,而且浏览器会自动打开一个窗口进行预览(如果没有……手动打开提供的链接吧)。如果没有开梯子,墙内访问速度会比较慢。

1
wrangler preview --watch 

预览结束后,可以按 Ctrl+C 中断预览。

若预览正常工作,运行下述命令即可将它发布到 Cloudflare Workers

1
wrangler publish

配置域名解析(路由)

我指定了自定义域名是 blog2.kukmoon.com,但是这个域名必须指定 DNS 解析才能访问。

在 Cloudflare 中添加一条 CNAME 记录,让 blog2.kukmoon.com 指向 blog.kukmoon.worker.dev
配置域名解析

注意:由于 Cloudflare Workers 要求自定义域名必须提供对应的区域 ID,因此自定义域名所使用的二级域名必须绑定到 Cloudflare

使用 GitHub Actions 持续部署

记得为博客源码所在的 GitHub 仓库 kukmoon_blog 添加一个 secret,名称为 CF_WORKERS_TOKEN,把 Cloudflare 的 API 令牌复制粘贴过去。

这篇文章[3]提供的 workflow 脚本因为缩进不准确和 persist-credentials: false 参数错误而无法运行,我修正了这两个,新的 workflow 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
name: My Hexo Blog # 名字随意
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version:
- 12.x
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
# 缓存 node_modules,缓存机制参见 GitHub 文档:https://help.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows

- name: Cache node_modules
uses: actions/cache@v1 # 使用 GitHub 官方的缓存 Action。
env:
cache-name: hexo-node-modules
with:
path: node_modules
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} # 使用 package-lock.json 的 Hash 作为缓存的 key。也可以使用 package.json 代替

# Wrangler 在构建时会在 workers-site 目录下执行 npm i,因此也要缓存这里的 node_modules
- name: Cache workers-site/node_modules
uses: actions/cache@v1
env:
cache-name: workers-site-node-modules
with:
path: workers-site/node_modules
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('workers-site/package-lock.json') }}

- run: npm i # 执行 Hexo 的依赖安装
# 完成 npm i 后,hexo 已经被链接到 node_modules 下的 bin 目录、并被注册在 Node.js 的 $PATH 中
# Hexo 博客的 package.json 中默认注册了这些 script:clean/build/deploy/server
# 因此,在目录下执行 npm run build 等同于执行 hexo g,但是不需要全局安装 hexo-cli
- run: npm run build

- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@1.1.0
with:
apiToken: ${{ secrets.CF_WORKERS_TOKEN }} # 前一步设置的 Secrets 的名称
# Wrangler Action 也支持使用传统的 Global API Token + Email 的鉴权方式,但不推荐

适用于 本博 的综合性的 workflow 参见此处[6]

番外:能不能把文件名命名为 file123.html.html

不能Cloudflare 社区 有人反映类似的情况会直接 404 (不好意思,源贴找不到了)。这说明 Cloudflare Pages 对双扩展名的文件进行 URL 跳转处理时存在 bug。由于正则表达式符合贪心算法,我估计 Cloudflare Pages 的开发者用正则表达式匹配 URL,结果把 URL 中左起第一个句点后面的字符都删除了。

图片版权

头图:Banner Image by milkusmaximus from Pixabay

参考文献


求扫码打赏
“我这么可爱,请给我钱 o(*^ω^*)o”

Hexo 博客部署到 Cloudflare Workers
https://blog.kukmoon.com/926771777b3b/
作者
Kukmoon谷月
发布于
2022年4月8日
许可协议