从零搭建个人博客:一次完整的踩坑记录

记录从多站点文档系统迁移到纯博客的完整过程,以及遇到的各种坑和解决方案。


背景

之前用 Mintlify 搭建了一个多站点文档管理系统(doc-manager),维护了多个子站点的文档。但实际需求变了——只需要一个个人博客

决定弃用文档站点,专注博客,把仓库改造成 Astro 个人博客。

迁移方案

核心思路:仓库根目录直接就是 Astro 项目,不走多站点构建管线。

最简原则:一个博客、一个仓库、一条部署链路。

踩坑记录

坑 1:astro check 扫描了构建产物

CI 的 astro checkoutput/ 目录里压缩后的 JS 文件也当 TypeScript 检查了,报了几十个错误。

💡

解决方案:在 tsconfig.json 中排除 scripts/output/dist/ 目录。

{
  "exclude": ["scripts/*", "output/*", "dist/*", "node_modules/*"]
}

坑 2:部署时 SSL 证书不存在

Nginx 配置引用了 Let’s Encrypt 证书,但新域名 blog.waterflow.site 还没有签发证书,Nginx 直接启动失败。

⚠️

解决方案:临时改用 webroot 模式签发证书,签发后再恢复 HTTPS 配置。

# 1. 临时配置允许 ACME 挑战
# 2. certbot certonly --webroot -w /var/www/certbot -d blog.waterflow.site
# 3. 恢复完整 HTTPS 配置

同时更新了 CI 部署脚本,以后新增域名自动签发证书。

坑 3:workflow_dispatch 不触发部署

CI 的 deploy job 条件是 github.run_number == 1,只在第一次运行成立。手动触发时 deploy 被跳过。

🔧

解决方案:改为 workflow_dispatch 始终触发,push 时 commit 消息含 “deploy” 也触发。

坑 4:PageFind 搜索全透明

这是最折磨的一个坑。搜索 modal 打开后背景全透明,内容完全看不清。

根本原因:Pagefind 的 CSS 文件通过 <link> 动态加载,在外部 <style> 之后生效,覆盖了我们的样式。

尝试过的方法:

尝试结果
外部 <style> 覆盖Pagefind CSS 加载后覆盖回来
加 !important选择器权重不够,匹配不到动态插入的元素
用 .dark 选择器暗色模式能部分生效,亮色模式仍然透明

最终解决方案:在加载 Pagefind CSS 之前,先用 JS 注入内联 <style> 标签,确保 !important 规则在所有 CSS 之后生效。

var style = document.createElement('style');
style.textContent = `
  .pagefind-ui { background: #fff !important; color: #171717 !important; }
  .dark .pagefind-ui { background: #171717 !important; color: #e5e5e5 !important; }
  .pagefind-ui input { color: #171717 !important; }
  .dark .pagefind-ui input { color: #e5e5e5 !important; }
`;
document.head.appendChild(style);

// 然后再加载 Pagefind CSS
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/pagefind/pagefind-ui.css';
document.head.appendChild(link);

关键:内联 <style><link> 之前注入,但因为是 JS 动态创建的,实际上在 DOM 中排在 <link> 之后,所以能覆盖。

坑 5:部署链路过于臃肿

原来的 CI 有 15 个步骤,包括 artifact 中转、Umami 部署等。实际上:

步骤是否需要
Inject meta tags不需要,Umami 已硬编码到模板
Generate sitemap不需要,Astro 内置 @astrojs/sitemap
Generate nginx config不需要,只有 1 个站点,直接写死
Umami 部署不需要,Umami 已稳定运行
Artifact 中转不需要,直接 SCP

简化后只剩 6 步:checkout → setup bun → install → type check → build → deploy。

最终成果

项目状态
HTTPSLet’s Encrypt 证书,自动续签
SEOSitemap、OG 图像、JSON-LD 全自动
搜索PageFind,亮色/暗色模式正常
分析Umami 自托管
部署每次 push 自动部署
CI6 步,约 45 秒完成

仓库地址:https://github.com/XXXIO1/blog

📝

总结:最小化是最好的架构。一个博客、一条部署链路、没有多余的中间环节。