把个人网站建成一颗星球:LuoPlanet 上线与 Ghost 域名迁移实录
从可旋转的微缩星球到真正上线的个人网站:记录 Vite、Three.js 的部署流程、Ghost 子域名迁移、旧邮件链接兼容,以及邮件配置和回滚中的实际经验。
2026 年 10 月 7 日,LuoPlanet 有了一个新的首页:一颗可以转动的微缩星球,星球上有工作站、太阳能板、天线和树木。访问 luoplanet.com,可以先认识我,再进入博客阅读文章。
这次改造的起点很简单:我想给 Web3D、全栈开发和持续探索新技术的兴趣,找一个更具体的表达方式。LuoPlanet 这个名字本来就带着“星球”的意味,微缩星球工作站便成了首页的主题。
但服务器上已经运行着 Ghost。新首页上线时,文章、管理后台、邮件和过去发出去的链接,都需要继续找到各自的位置。这篇文章记录的,就是从空代码仓库到第一版上线,以及把 Ghost 从主域名迁到博客子域名的过程。
先让第一颗星球落地
第一版使用 Vite、TypeScript 和 Three.js,场景中的星球与建筑由程序几何生成。页面支持拖动、方向键旋转、自动旋转和视角复位,文字介绍与博客入口使用普通 HTML。
我把第一版的范围定得比较小:先把首页和部署流程跑通,再逐步增加作品和实验。现在的“Web3D 实验室”仍标注为筹备中,博客通过链接进入,尚未接入 Ghost Content API 的文章列表。
除了 3D 画面,这次还处理了几个基础体验:手机布局、减少动态效果偏好、WebGL 不可用时的静态图片,以及 JavaScript 被禁用时仍可阅读的文字和链接。原网站的备案信息也保留在了新页脚。
给工作站、博客和后台各留一个地址
最后确定的域名分工是:
- luoplanet.com:个人工作站,展示 3D 场景和个人信息。
- blog.luoplanet.com:Ghost 博客,负责文章阅读、会员和订阅。
- ghost.luoplanet.com/ghost/:继续使用原有的 Ghost 管理后台。
它们仍在同一台服务器上,由 Nginx 按访问域名处理请求:工作站读取静态文件,博客和后台转发给现有 Ghost 容器。Ghost 的数据库、内容卷和 OSS 存储适配器继续沿用。
访客 → Cloudflare → Nginx
├─ luoplanet.com → 工作站静态文件
├─ blog.luoplanet.com → Ghost 博客
└─ ghost.luoplanet.com → Ghost 后台
↓
原有数据与存储这样,更新星球场景和发布文章就各有一条流程。前端更新走代码仓库和发布脚本;写文章仍然在 Ghost 后台完成。
从空仓库到可重复发布
工作站使用独立的 luoplanet-workstation 仓库,管理页面、3D 场景、公开素材、构建配置和部署脚本。服务器凭据、Ghost 数据库和内容备份留在各自的运行环境里。
Vite 构建后的产物是 dist/,由 Nginx 提供访问。Three.js 在访客浏览器中渲染,因此这版首页无需额外运行一个常驻 Node 服务。npm run preview 只用于本地检查构建结果,这也符合 Vite 的静态部署方式。
发布过程被整理成脚本:安装依赖、检查 TypeScript、构建、打包,通过阿里云 Workbench CLI 上传,核对 SHA-256,然后切换版本。服务器上的目录结构大致是:
luoplanet-workstation/
├── releases/
│ ├── 版本 A/
│ └── 版本 B/
├── current → releases/版本 B/
└── shared/assets/current 指向正在提供访问的版本。新文件完整上传后再切换链接,发布检查失败时恢复之前的版本。带内容哈希的资源保存在共享目录中,让已经打开的旧页面仍能加载对应文件,也让回滚后需要的资源继续可用。
仓库还配置了 GitHub Actions,用于构建检查和保存产物。当前实际生产发布由本地脚本调用 Workbench 完成,推送 GitHub 并不会自动发布生产站点。
Ghost 搬到子域名,改动集中在入口与公开地址
开始服务器操作前,我已经在 Cloudflare 配置好了 blog 子域名。随后核对证书,确认现有 Cloudflare Origin 证书覆盖根域名和 *.luoplanet.com,可以继续用于 Cloudflare 到源站的 HTTPS 连接。
修改前,先备份 Ghost Compose 配置、Nginx 配置、数据库、内容卷和存储适配器,并检查备份压缩文件的完整性。接着,在 Ghost 的 Compose 环境变量里调整公开地址:
url: https://blog.luoplanet.com
admin__url: https://ghost.luoplanet.com这里需要改变的是 url。独立后台原本就在 ghost.luoplanet.com,因此 admin__url 保留。Ghost 官方把网站 URL、后台 URL 和邮件配置分别管理;使用 Docker 时,可以通过对应环境变量覆盖这些设置。参见 Ghost 配置文档。
然后让 Nginx 为博客子域名转发请求、为主域名提供新首页,检查配置语法,重建 Ghost 容器并等待其就绪,再 reload Nginx、检查各个入口。迁移过程中没有重装 Ghost,也没有批量重写文章数据。
容易漏掉的,是已经发出去的链接
主域名换成工作站以后,旧的文章地址依然可能存在于收藏夹、搜索结果或邮件里。服务器需要将这些路径转到博客,并保留原来的查询参数。
这次兼容的范围包括旧文章、图片、RSS、会员入口、退订和追踪路径。例如:
luoplanet.com/coming-soon/
→ blog.luoplanet.com/coming-soon/
luoplanet.com/members/?token=…&action=signin
→ blog.luoplanet.com/members/?token=…&action=signin工作站当前使用页面内锚点导航。Nginx 为首页和静态资源安排明确的路径,其余旧博客路径转到 blog,避免旧文章请求误入一个与它无关的首页。
还有一种链接,仅靠 Nginx 处理不到:https://luoplanet.com/#/portal/account。URL 中 # 后面的 fragment 不会发送给服务器,旧邮件里的订阅管理入口就可能落到新的星球首页。
因此,新首页在加载应用前先判断 Ghost 的 Portal 链接,保留查询参数和 hash 后转到博客。核心逻辑如下:
if (/^#\/portal(?:[\/=?]|$)/.test(location.hash)) {
location.replace(
'https://blog.luoplanet.com/' + location.search + location.hash
);
}普通的 #about 等工作站锚点仍留在首页。域名迁移到这里,才开始照顾到访客从不同地方抵达网站的情况。
网站域名改了,Mailgun 也要跟着改吗?
这是这次迁移中我特别关心的一点。核对实际配置后,答案是:这次可以保留现有邮件发送配置。
网站公开地址和邮件发送域名承担不同的职责。我的配置分为两部分:
- 事务邮件:登录、注册等邮件通过阿里云 SMTP 发送,现有发件地址为
[email protected]。 - Newsletter:通过 Mailgun 发送,Mailgun 中配置的发送域名为
mg.luoplanet.com。
博客迁到 blog.luoplanet.com,不要求把上述发送域名一起改成 blog。现有 SMTP 配置、Mailgun API Key,以及 SPF、DKIM 等邮件 DNS 记录都可以继续使用。
还核对了一个细节:当前 Newsletter 没有单独填写发件地址,已安装的 Ghost 版本会使用有效的 mail.from。因此,在这套配置下,网站 URL 改变后默认发件地址仍然是原来的地址。
检查结果是:Mailgun 域名处于 active 状态,域名级 webhook 列表为空;切换前后的 SMTP 连接与认证都通过验证。这次没有实际发送测试邮件或群发,所以这些检查不能替代收件箱投递验收。
第一次切换后的检查失败了,回滚发挥了作用
这次上线并非一次操作就全部通过。第一次切换后,紧接着执行的博客源站健康检查没有通过,脚本自动恢复了原来的 Ghost 和 Nginx 配置。
后续检查确认原博客已恢复。随后,我在脚本中加入了新路由的就绪等待:Nginx 收到 reload 命令后,给新配置生效和工作进程切换留出有限的检查窗口。第二次切换通过了验证。
这件事提醒我,发布流程需要检查服务最终呈现的结果。除了命令退出码,还要看页面是否可访问、是否返回预期内容、博客的 canonical 是否已经指向新地址。失败时也需要有明确的恢复路径。
上线之后,检查页面与它周围的连接
最终检查覆盖了以下内容:
- 主域名展示新工作站,博客子域名展示原有文章,独立后台可以打开。
- 桌面和手机上的 3D 场景正常加载,方向键、自动旋转与复位可用,页面没有资源 404 或 JavaScript 错误。
- WebGL 不可用、上下文丢失或 JavaScript 被禁用时,基础内容与链接仍然可用。
- 旧文章、RSS,以及带查询参数的会员、退订和追踪入口按配置跳转;历史 Portal hash 链接也能抵达博客。
- SMTP 认证与 Mailgun 域名状态正常;原有 Ghost 自动更新脚本仍检查独立后台地址,不受首页更换影响。
其中,会员与退订检查针对入口和参数保留,没有实际执行用户账户操作。备份文件也做了完整性检查,但尚未进行一次完整的数据恢复演练。这些是后续维护时需要继续补足的验证。
工作站已经开门,接下来慢慢建造
现在,第一版星球已经可以访问,博客也有了独立的阅读空间。下一步,我想在星球上增加真实的 Web3D 实验和项目案例,再通过 Ghost Content API 将最新文章接入“技术观测台”。
随着内容增加,也需要继续观察模型体积、移动端体验和加载成本。对这个个人网站来说,场景会逐渐丰富,而写作和发布仍应该保持顺手。