#77 搭建 Ghost 全流程

2023 年不懂代码搭建 Ghost 是什么体验

Design Scenes 第 77 期封面图,采用了乐高拼接样式

你好啊,这里是许久不见的 Design Scenes,别来无恙。

在重新开始写 newsletter 前,一件确定的事便是考虑更换平台。竹白的基础功能有些波动,最主要的是产品几乎不再更新,这在做产品的眼中基本已经☠️了。剩下的邮件服务平台很多都是以盈利目的使用,自身大多需要月供一定的费用。最终在 Substack 和自己搭建 Ghost 之间抉择。Telegram 上的 @informavore 告诉我可以试试 Fly.io + Ghost 组合,被我第一时间本能地拒绝了。但由于垂涎 Ghost 已久,本着「反正建不好有 Substack 兜底」,还是着手试了试,果然不会一帆风顺。

本文较长,可以访问原文使用目录功能查看。希望对像我一样少有代码接触也想建立自己的 Ghost 的读者有所帮助。

初次接触 Ghost

高中时我对 CMS 博客理解基本就是 WordPress,用过一小段时间 Typecho,后面就对 Hugo、Next.js 之类的没什么印象。时过境迁,CLI 中几行命令即可迅速部署好一个博客。推荐下面这篇文章中的流程。

在fly.io免费运行Ghost博客:安装、备份和恢复
这篇文章介绍了在fly.io上免费运行Ghost博客,备份和还原数据的方法。备份和恢复数据对其他服务器上的用例具有参考意义。

这步需要注意的是 Fly.io 的服务器节点区域选择,就近最好。依梯子那边线路延迟来看,我这里选择的是香港节点(hkg),更多区域节点详见文档

Fly.io 可以随时对配置升级,免费配置中只有 256MB 内存有点捉急。Ghost 官方推荐是 1GB,网站在刚搭建完基本没做什么时内存占用都在200MB 左右,偶尔还会出现 502 错误,Fly.io 官方也向我发送过崩溃报错邮件……所以我升级到了 512MB,算是稳定下来。

按照文章顺便配置了自己的域名,没有乱七八糟额外收费 Fly.io 这点也很不错。改域名后需要重新部署。

Ghost 后台很简洁,大致填写了一遍表单后,剩下的就是配置邮件和主题样式。

Mailgun 的小心思

Mailgun 是 Ghost 官方那个推荐的发件系统,后台也集成了相关功能,简单配置发送域名和 API 就可以使用——但没想象的那么简单。

首先 Mailgun 的注册就把我卡住了,账号需要邮箱和手机号验证。虽然支持 +86 短信发送,但是目前为止一封验证短信也没收到。之后搜到了 Her Blue 这篇文章。

终于解决了会员注册的问题
历经千辛万苦,终于在今晚,把网站的会员注册功能给解决了。 现在,你可以点击「成员资格>立即订阅」来注册自己的账号,就可以参与文章页的评论了。 一些过程 早在之前,这个功能是正常的,小站也有十几个人的注册用户,但是有一次在搬迁服务器之后,这个功能就出错了,大概原因是因为服务器的25端口被关闭了,无法解封。 之后一直想解决这个问题,找DIN帮忙,也找一间生活的博主讨论,最终放弃了之前的SMTP邮件方式,转到Mailgun的邮件服务商。 但这一转也是历经多重磨难: * 注册Mailgun账号,在没有搭梯子的情况下,注册页面的「验证码」模块是加载不出来的,所以「确认注册」的按钮是无法按…

最后我也是邮件客服,由于时区等了 10 几个小时后才解决。

Mailgun 套餐改过很多次,2023 年版本是:首先 Mailgun 有一定的免费配额,也就是 Free Plan,每月免费 5000 封邮件,看着很吸引人。但是默认只能使用 sandbox 前缀域名,而且只能发送给 Authorized Recipients(认证的接收者),且最多 5 个人。Mailgun 解释说这可以用做测试。完全体需要掏出信用卡升级。

然而点击 upgrade 后,却发现和 Ghost 官方说的不一样。

Mailgun 在 2023 年 9 月 30 日的价格页面截图

上来就 35 美元每月直接差点劝退我,我又找了几篇文章才发现,很多小型博主使用的是 Flex Plan,我擦亮眼镜也没发现这 8 个字母在哪。又是一番搜索,发现了这个帖子。

[solved] Mailgun has quietly removed their “pay as you go” offer. Any new option to be integrated with Ghost’s native newsletter?
Yes, you can. Start the trial, and then you can downgrade to the Flex pay as you go model.

原来 Mailgun 早就取消了直接升级 Flex Plan 的渠道,需要添加信用卡后,升级到 Foundation (Trial) 后再 downgrade 即可在下个月变为 Flex Plan。高,真的高。

接下来就是按照官方流程,添加新的自定义域名,生成新的 Mailgun API keys,填写到 Ghost 后台。这段流程中,无论是 DNS 配置还是 Ghost 调用 Mailgun 都有一定的延迟,所以测试邮件时别像我一样心急……

Ghost 会员邮件配置

后台配置好 Mailgun 后文章可正常推送到目标邮箱。但是前台会员注册/登录的 maigc link 验证邮件提示无法发送。回到 Ghost 安装的文章,原因是 fly.toml 一些字段没配置。

mail__from = "noreply@example.com"
mail__options__auth__pass = "<YourMailgunPassword>"
mail__options__auth__user = "postmaster@example.com"
mail__options__host = "smtp.mailgun.org"
mail__options__port = "465"
mail__transport = "SMTP"

在 Ghost 的 FAQ 页面可以看到,Ghost 的发信分为两种:

  • 1 对 1 的事务邮件,使用 SMTP,一般用于会员注册/登录;
  • 1 对多的批量邮件,使用 Mailgun 的 Bulk API,一般用于群发 newsletter;

上面 toml 文件中的用户和密码使用的不是 Mailgun 账号密码,而是 Sending > Overview > SMTP credentials 中的账号密码。要查看密码,点击 Reset Password 才可以……这个交互逻辑也是闻所未闻。听说这还是 Mailgun 新版后台界面,到底改了什么😅。

Mailgun 后台 SMTP credentials 设置页面
Mailgun 后台 SMTP credentials 设置页面

这样 SMTP 部分便部署完毕, 过程中也加深了对配置文件 fly.toml  的认识 (毕竟是没用过 docker 的人🥹)。

Ghost 配置之路

不同于 Framer 设计师能亲自上手造车,Ghost 主题需要「老一套」代码编译。好在结构和其他博客系统差不多,官方文档覆盖比较全面,Handlebars 门槛也不高,多翻看几次也能稍微理解一些表面。

本地部署

我使用的是古典主义 Ghost 后台上传压缩包方式更换主题,为了改主题方便一点需要本地部署一个 Ghost,然后直接修改内部主题文件。

How to install Ghost locally on Mac, PC or Linux
A detailed local install guide for how to install the Ghost publishing platform on your computer running Mac, PC or Linux. Ideal for Ghost theme development.

这个很简单,安装 Ghost-CLI 后,cd 到新建好的目录直接安装。很快,都不用配置数据库。

主题本地化

挑选一个合适的主题很重要,Ghost 有很多优雅但又价格不菲的主题,但囊中羞涩。好在 Ghost 官方有一些不错的免费主题,便选择了 Journal 这一款。

Ghost 支持文本和一部分 handlebars 表达式的本地化。

Ghost Handlebars Theme Helpers: translate
Discover how to translate content using your Ghost theme and the translate helper. Read more about translations in Ghost 👻

主题根目录中 locales 文件夹里面的 json 文件便是主题中的可定制翻译文本。有最好,没有的话可能就得像我一样挨个找出来新建……还要把对应的文本用 {{t }} 方式包起来。

主题 Journal 的zhcn.json 翻译文本
主题 Journal 的 zhcn.json 翻译文本

之后在 Ghost 后台设置 > General > Publication Language 中填上 zhcn 即可生效。但有一些组件 Ghost 尚不支持定制,详见文档。这些文档都存在于核心服务中,还没有单拿出来定制。

比较遗憾的是 Ghost 无法深度定制邮件模板 CSS 样式,5.65.1 版本目前可以在 /versions/5.65.1/node_modules/@tryghost/email-service/lib/email-templates/template.hbs 找到模板,如果要更改其样式可在上层文件夹里的 ../partials/styles.hbs 修改,但也只能硬改,无法有效地编译。

另外在 /versions/5.65.1/core/server/services/mail/templates/ 中能发现很多邮件模板,/versions/5.65.1/core/server/services/newsletters/emails/verify-email.js 中能看到验证邮件的字段。理论上都可以更改,但是一旦更新版本这些改动又有概率重置,得不偿失。我只在本地测试了一下,没有上线改动。

改动这些文件需要执行 ghost restart 生效。

添加文章目录

Ghost 有一个栏目介绍自家系统的拓展用法。我比较纳闷为啥有些功能为啥不直接做进系统里……想起之前文档里有说过,如果要支持其他 ESP 请自己提 pull request,看来其他功能也是如此。

目前先打算添加一个比较基础的目录功能,使用的如下文章方法。

How to add a table of contents to your Ghost site
Let your readers know what to expect in your posts and give them quick links to navigate content quickly by adding a table of contents with the Tocbot library.

过程中碰到了两个点:

一是CSS 调用问题:主题调用的是 assets/built/screen.css 文件,而不是 assets/css/screen.css,前者需要使用 Yarn 和 Gulp 编译后者生成。为了省事我直接把样式写字了 hbs 文件头部。

二是布局问题:按照文章的代码会导致无法浮动固定在指定位置。

Arc 浏览器检查器中的主题 Journal 的文章头部结构

原文涉及侧栏的样式代码:

@media (min-width: 1300px) {
     .gh-sidebar {
        position: absolute; 
        top: 0;
        bottom: 0;
        margin-top: 4vmin;
        grid-column: wide-start / main-start; /* Place the TOC to the left of the content */
    }
   
    .gh-toc {
        position: sticky; /* On larger screens, TOC will stay in the same spot on the page */
        top: 4vmin;
    }
}

如果给 .gh-sidebar 设置 position: sticky; 时就会因 css grid 而影响文章的首段行高,后来去查阅了一下 MDN 发现是容器高度问题,.gh-sidebar 在主题默认样式中 height:max-content; 内容多高容器就多高,当然无法滚动固定。更换为 height: auto; 即可使容器高度和文章一致,solved。为了不妨碍主题样式,我直接重新起了个 class 名。

后面如果使顶部导航栏浮动的话,相对高度改变,.gh-toc 也要改一下数值,整体变为:

@media (min-width: 1300px) {
     .article-sidebar {
        position: absolute; 
        top: 0;
        bottom: 0;
        margin-top: 4vmin;
        height: auto;
        grid-column: wide-start / main-start; /* Place the TOC to the left of the content */
    }
   
    .gh-toc {
        position: sticky; /* On larger screens, TOC will stay in the same spot on the page */
        top: 16vmin;
    }
}

Ghost 这个 Do more 栏目还有实践,如果之后代码使用很多的话也考虑优化一下。阅读进度条先不打算加,毕竟不加 100% 不会减慢访问速度。

目前的主题已经传到了 GitHub 上,可自行参考。

GitHub - fenxer/Journal-zhcn: Ghost 主题 Journal 定制版
Ghost 主题 Journal 定制版. Contribute to fenxer/Journal-zhcn development by creating an account on GitHub.

加速 JsDelivr 和 Gravatar

Ghost 很多组件 js 托管在 JsDelivr,虽然现在也能访问,但是速度较慢,我这测试不用梯子时,有的 js 载入 10s 有余。

Ghost 配置文件 default.json
Ghost 配置文件 default.json

/versions/[当前版本号]/core/shared/config/ 文件中,default 配置了 Ghost 许多核心功能。如上图为 JsDelivr 托管的一些文件。可以把这些文件都下载下来放到自己服务器上,也可以把所有 cdn.jsdelivr.net 替换成 fastly.jsdelivr.net。两种方法可分别参考下面两篇文章。

Ghost 的两个优化点
Ghost 有一些默认的外部资源引用可能会造成部份访客的体验下降,为了让各地访客都能有一个不错的速度,纯小白用户建议修改两个地方。

我使用的是前者方法,速度更有保障,但每次更新版本可能需要覆盖更新。将这些 js 文件下载下来后(除了 editor,会导致后台编辑器不可用),如何传到 Fly.io 上又是一难题——所有文章都没写过。

这个时候就该说明一下为什么推荐第一篇文章安装 Ghost 了,原文第 6 节提到了如何备份和恢复 Ghost,其中使用了 SFTP,查询关键词,果然是这个。

fly sftp
Documentation and guides from the team at Fly.io.

照葫芦画瓢,原文有些方法不适用,但还是成功进入到 VM 文件内部:

cd [安装路径]
flyctl auth login

先登录,然后 shell 命令中,可以多种方法进入自己 VM 的存储卷,比如应用名、区域或组织名等等,详见帮助说明

flyctl ssh sftp shell -r [地区代码]
flyctl ssh sftp shell -a [应用名]
flyctl ssh sftp shell -o [组织名]

进入后会显示红色的 >> 符号,然后就可以执行各种命令了。

cd #改变目录
ls #列出当前文件列表
put #上传
get #下载
chmod #权限设置
将下载好的 js 文件传到 Fly.io 后的文件夹列表
将下载好的 js 文件传到 Fly.io 后的文件夹列表

然后打开 fly.toml,按照文章把上传后的 js 路径写到 [env] 里去。注意这里要加上双引号,以符合 Fly.io 这边语法。

到这里可以顺手把 gravatar 地址改了,官方源不用梯子无论如何也访问不了,裂图效果很糟心。这里使用的是这篇文章提到的镜像源:

[env]
gravatar__url = "https://use.sevencdn.com/avatar/{hash}?s={size}&r={rating}&d={_default}"

最后重新部署一遍就可以了。

客服邮件备置

Ghost 的 Portal settings设置中可配置客服邮箱,为了隐私性和统一性,我打算再开个域名邮箱。这一看才发现 QQ 的免费域名邮箱早已不再提供,被企业微信融合。鉴于目前是早期试运行阶段,付费域名邮箱也并非首选。考察一番后,发现飞书在提供免费域名邮箱,只需要新创建一个企业,未认证的也可以。

创建后在企业后台 > 产品设置 > 邮件中直接添加域名,跟着流程走就可以。最后在成员列表设置邮箱即可在飞书收信。如果更方便点,还可以在设置里自动转发到常用邮箱。

这里我使用 hi@fenx.work 作为客服邮箱。

客服邮箱配置好后,我在网站页面底部加上了「反馈」入口,它的本质是一段 mailto 链接:

mailto:hi@fenx.work?subject=[网站反馈]&body=请写下反馈页面地址和具体问题

其中 subject 和 body 作用是默认在标题和正文加上一段文字,辅助用户更好地反馈。

更近一步优化的话,验证邮件也可以帮忙打开,Growth Design 有一期讲的是这事,利用邮箱的筛选功能配置网址直接找到验证邮件。

其他网站设置

Design Scenes 更换到塔状 logo,以塔的各种形式重新诠释。使用了 Newsagent 字体。颜色也比之前更亮了一点,以匹配网页上 sRGB 的低亮度。

Design Scenes 黑白色 logo

剩下就是一些页面文案撰写工作,花了一些时间。

哦对了,还有邮件批量导入。Ghost 提供了 csv 模板,也是比较方便的。

部署 umami 分析

如今已不是Google Analytics 横行的时代,大量隐私优先的统计工具出现。我在之前 newsletter 里提到过 creativerly 的文章:

A list of privacy-friendly Google Analytics alternatives.
If you want to get insights about the traffic happening on your website you probably came across Google Analytics. Let me tell you: it is bloated, hard to use, and scraping user data. In this blog post, I will show you a list of privacy-friendly and ethical Google Analytics alternatives.

大部分平台依然需要月供十几美元来支付「隐私费」,使用较为广泛且有开源的 Plausible 和 umami 成为半决赛选手。Plausible 使用 docker 设计搭建,无论在 DigitalOcean Droplet 还是 Fly.io 上都需要额外再掏钱。所以我选择了可以直接部署在 Vercel 的 umami

在 Vercel 部署 umami 网站统计及报错解决
Umami 是一种简单、快速的网站分析替代品,可替代 Google Analytics。

网上有很多相关文章,基本流程大同小异,建立 supabase 数据库,Fork 官方库后到 Vercel 配置环境变量,部署上线。推荐这篇文章是它帮我解决了一个啼笑皆非的错误。

我在最后一步部署时,Vercel 总是提示 unable to connect to the database 报错,因为要填写的 string 一共也没几个所以很纳闷……后来看到这篇文章时突然察觉到,是不是我填写 DATABASE_URL 时密码写错了!原本要求的是 [YOUR-PASSWORD] 全部替换为密码,但是我保留了两边方括号……呃。

更改分析域名与脚本名

在查找资料时发现有人提到 *.vercel.app 域名国内访问问题,无 cookie 统计已经不是很准确,我想尽量排除一些干扰要素。于是到 Vercel 项目设置 (Project Settings) 页面,加上了自己的二级域名。这下域名那边的 DNS 解析已经两页之多了😅。

Umami 现在默认使用的是 script.js,虽说不像之前 umami.js 那么容易被去广告插件识别,但说不准什么机制会有干扰。Umami 提供了更改 js 名这一服务,直接在 Vercel 项目设置 Environment Variables 中添加一条 TRACKER_SCRIPT_NAME 即可。命名规则如下:

TRACKER_SCRIPT_NAME=           # Unset. By default, the script is fetched from: /script.js
TRACKER_SCRIPT_NAME=custom     # Fetched from: /custom
TRACKER_SCRIPT_NAME=custom.js  # Fetched from: /custom.js

至此,跟踪代码已经自定义为:

<script async src="https://[自定义二级域名]/[自定义脚本名]" data-website-id="[网站id]"></script>

Umami 不使用 cookie,不搜集带有个人标识的数据,虽然不准确(比如我自己的活动数据也会计入),但这样你的数据隐私不会被跨站利用。Plausible 写过一篇统计原理可以看看:

58% of Hacker News, Reddit and tech-savvy audiences block Google Analytics
Is Google Analytics still useful and how accurate are its stats? How much data is missing from Google Analytics due to adblockers and privacy-friendly browsers?

另外 CloudFlare 也有一款免费的网站分析产品,同样是隐私优先,但省事许多,之后看看是否有机会尝试一下。

结语

至此 Ghost 平台首发版本初步搭建完毕,本文也将作为第一次群发邮件测试。7k 字内容不知道在邮箱哪里会被截断……实际工作量远没有文章中那样谈笑风生,期间至少有 3 次我觉得还是去 Substack 吧。但没到山穷水尽那一刻总觉得不是放弃的时机,磕磕绊绊也算前进了。

另外在本次流程中没有使用带有 LLM 的 AGI 对话工具,依然全程使用 Google 搜索。我之后在 Poe 和 Bing 试了下,不是出现「幻觉」就是重复信息,帮助不多。


如果你觉得文章对你有些帮助,可以请我的猫吃罐头 ↓

带有微信赞赏码和文字 A kind and compassionate act is often its own reward. 的横幅图像

订阅 Design Scenes

发布最新文章时,会以邮件通知你
zelda@link.com
订阅