跳到正文
Hacker News· meysamazad·· 7 天前AI 评分12

如何用 Bunny.net Middleware Edge 脚本托管 Quartz 静态站点

Host Quartz Static Site on Bunny.net with Middleware Script

AI 导读

针对 Quartz SSG 按页面类型输出不同 HTML 结构(扁平 about.html 与 folder/index.html)导致 Bunny Edge Rules 无法区分的问题,作者用 Bunny CDN Middleware Edge 脚本实现了路由:先尝试加 .html,失败则回退到 /index.html,同时跳过首页和带扩展名的静态资源。

正文 · AI 翻译

了解我如何使用 Bunny CDN Middleware Edge 脚本绕过 Quartz SSG 输出其公共 HTML 站点文件的方式,修复 Edge 规则的局限性。

前言

我写过一篇笔记,关于在尝试将我的 Quartz 站点托管到 Bunny.net Storage and CDN (联盟链接) 时遇到的 一个问题。我联系了 Bunny 支持团队,询问可能的建议,然后当晚就睡了。

回顾一下,我的问题是:

  • Quartz emits two different HTML file structures depending on page type:
    • 普通页面(例如 about.md)被输出为扁平的 about.html 文件。
    • 文件夹/索引页面被输出为 about/index.md,或者自动生成的文件夹列表被输出为 folder/index.html 而不是 folder.html。
  • 使用 Bunny Edge Rules(基于模式/优先级)来进行扁平重写行不通,因为它不知道如何区分不同的页面类型。它无法进行基于文件系统的检查(例如 Nginx 中的 try_files),也无法在执行重写规则之前检查文件是否存在。

这篇文章适用于那些使用静态主机(可以是不是 Bunny Storage 的提供商)托管其 Quartz 构建的站点,并希望使用 Bunny CDN 来绕过 Quartz 输出其 HTML 文件结构的方式的人。

也就是说,托管一个 Quartz 站点,而不是:

  • Cloudflare Pages
  • GitHub Pages
  • GitLab Pages
  • Vercel
  • Netlify
  • Self-hosting / Web Server with
    • Nginx
    • Apache
    • Caddy

可能的修复方法

Bunny 客户支持在两小时内回复了(他们的回复总是超快且有帮助!),并给了我两种可以尝试的方法:

1) Edge Rule

使用一个 Edge Rule,在初始 404 后进行 Origin Retry(如果添加 .html 不起作用,尝试添加 /index.html)。他们提供了一个工作流程,我会在这里分享给想要尝试的读者:

Flowchart showing how to use a Origin Retry Bunny Edge rule

(注意:我没有测试这个方法)

以下是你需要在 Edge Rule 配置页面中输入的内容:

操作:

  1. 覆盖 Origin URL

    Origin URL:https://your-origin.com{{path}}/index.html

  2. 重试 Origin

条件:

如果匹配全部

  • 状态码:匹配任意:404
  • Origin 重试尝试次数:匹配任意:0(零)

2) Edge Script

不过,为了更好的可扩展性,请使用 Edge 脚本 来实现更程序化的路由逻辑,从而无需维护显式的目录列表。

我现有的 Edge 规则

在某些场景下,仅使用 Edge 规则就能奏效(我下面的方法):

  • 你不使用 Quartz 索引/文件夹页面,或者
  • 你有数量稳定的文件夹/目录页面,可以“设置后就不用管”

由于文件夹索引页面是可选的(参见 文件夹和标签列表 和 文件夹页面),使用几条 Edge 规则就足够了,只需将 .html 添加到除首页和资源之外的所有扁平页面。

供你参考,以下是我在 Bunny.net Storage + CDN 上为 Quartz v5.0.0 临时使用的 Edge 规则。提示:优先级很重要!如果你没有任何文件夹索引,跳过第一条规则。如果你的索引页面数量有限且稳定,请相应地将页面添加到第一条规则中(我这里有 /blog 和 /notes;请将其更改为你的索引页面 slug)。

Screenshot of Bunny.net Edge Rule config page with three rules to handle Quartz-built sites

但我想找到一个可扩展的解决方案——每次有新的索引/文件夹页面时手动添加到第一条规则,这不是我想做的事情。趁着我对 Quartz 的有限理解还记忆犹新,我决定尝试 Bunny Edge 脚本来解决这个 Quartz 托管问题。

Bunny 客户支持建议的第二个选项感觉更可靠,尽管相比免费的 Edge 规则需要额外费用。我觉得我永远不会超过第一个价格档位(100 万次请求),所以我很乐意支付使用 Edge 脚本的额外 0.22 美元基础费用。

Bunny.net 的最低月费为 1 美元/月。我最近写了一个/costs页面,如果你想了解更多。

Bunny.net price breakdown

加上新的额外 0.22 美元,我仍然在最低限额之内!👍

修复需求

我需要一个中间件脚本来执行以下操作:

  1. 不要对主页或任何资源做任何处理(即排除任何带文件扩展名的内容)
  2. 假设第一个请求是纯 HTML 文件,添加 .html
  3. 如果第 2 步没有成功,则回退到添加 /index.html
  4. 确保静态资源(CSS/JS)保持不变,这样网站就不会崩溃

提示

在使用 Edge 脚本之前,请禁用 Edge 规则以防止操作重叠或错误。并且更改后一定要清除缓存!

寻找现有解决方案

但我对Deno没有经验,这是 Bunny Edge 脚本使用的基于 JS 的语言!学习 Deno 不在我的计划之内(掌握 TypeScript 已经让我够呛了!),所以我开始搜索是否有人写过关于在 Bunny.net(或其他静态托管)上托管 Quartz 构建的网站,而不是使用本文开头提到的托管提供商。

令我惊讶的是,居然真的有一篇文章,感觉就像是专门为我写的,作者是Grimtech — 在 Bunny CDN 上托管 Quartz 博客。我立即尝试按原样使用他的脚本,但不幸的是它没有用;也许是因为他用的是Quartz v4.5.2,而我用的是v5.0.0?

在 Bunny 脚本的浏览器内编辑器中,有一个实时预览窗格,让我可以测试请求 URL,看看规则是否被正确应用和路由。我尝试自己调整脚本以满足我的四个需求;但说实话,我完全不知道自己在做什么。🙃

我所做的更改存在语法问题,并且不断因重定向循环和 404 而失败。我不得不放弃,并求助于 LLM:Claude Haiku 4.5。我给了它我的需求,经过几次编辑和测试的尝试后,我得到了一个可行的解决方案!这里供参考:

Quartz 的中间件脚本修复

免责声明

一个 LLM(通过 Duck.ai 的 Claude Haiku 4.5)在我的指示下帮助生成了下面的脚本;我通过多次测试运行手动修订、改进并验证了它。使用前请先审查。

 1import * as BunnySDK from "@bunny.net/edgescript-sdk"
 2
 3async function onOriginRequest(context: { request: Request }) {
 4  const url = new URL(context.request.url)
 5  const pathname = url.pathname
 6
 7  // Skip homepage and static assets with extensions
 8  if (pathname === '/' || pathname.includes('.')) {
 9    return context.request
10  }
11
12  const headers = new Headers(context.request.headers)
13  headers.set('X-Original-Path', pathname)
14
15  // Initial attempt: 
16  // If it ends with a slash, assume folder index. If not, assume flat HTML file.
17  if (pathname.endsWith('/')) {
18    url.pathname = `${pathname}index.html`
19  } else {
20    url.pathname = `${pathname}.html`
21  }
22
23  return new Request(url.toString(), { ...context.request, headers })
24}
25
26async function onOriginResponse(context: { request: Request, response: Response }) {
27  if (context.response.status === 404) {
28    const originalPath = context.request.headers.get('X-Original-Path')
29    if (!originalPath) return context.response
30
31    const url = new URL(context.request.url)
32
33    // Fallback 1: User requested a folder without a slash (/blog), but it failed. Try index.html.
34    if (!originalPath.endsWith('/') && url.pathname.endsWith('.html')) {
35      url.pathname = `${originalPath}/index.html`
36      return fetch(url.toString(), context.request)
37    }
38
39    // Fallback 2: User requested a flat page with a slash (/about/), but index.html failed. 
40    // It's a flat page, so 301 redirect to the clean URL (/about) to keep asset paths intact.
41    if (originalPath.endsWith('/') && url.pathname.endsWith('index.html')) {
42      const cleanPath = originalPath.slice(0, -1)
43      const flatTestUrl = new URL(url)
44      flatTestUrl.pathname = `${cleanPath}.html`
45      
46      const flatCheck = await fetch(flatTestUrl.toString())
47      if (flatCheck.status === 200) {
48        return new Response(null, {
49          status: 301,
50          headers: { "Location": cleanPath }
51        })
52      }
53    }
54  }
55
56  return context.response
57}
58
59BunnySDK.net.http
60  .servePullZone()
61  .onOriginRequest(onOriginRequest)
62  .onOriginResponse(onOriginResponse)

结论

我非常激动它能正常工作,因为我以为我不得不放弃使用 Bunny 了!使用Quartz对我来说是一段相当艰难的旅程;但我想这就是每次开始学习新技术时的感觉。WordPress 和 Hugo 一开始都让人困惑——我讨厌其中一个但喜欢另一个(顺便说一下,是 Hugo)。

我不认为我会像喜欢 Hugo 那样喜欢 Quartz 的后端,但没关系;作为静态网站生成器,我对 Quartz 的喜爱远胜于WordPress,无论哪一天都是如此!

我近期会再写一些关于 Quartz 自定义的内容,如果你感兴趣,可以考虑通过 RSS/Atom 订阅 subscribing。如果这篇文章对你有帮助,请考虑使用我的 Bunny.net 推荐链接,或者给我 打赏/买杯咖啡。感谢你的支持,希望这对你有帮助!

—Naty

来源:Hacker News · burgeonlab.com