暂不支持移动端访问

管理后台与 GitHub 内容发布

1893 字 9 分钟
AI 摘要

说明 AstroBlog 管理后台如何编辑内容,并通过受保护的 GitHub Proxy 发布到静态站点。

AstroBlog 的后台是对仓库内容的受保护编辑界面。文章、动态、书籍、友链、导航和配置最终仍然写回内容文件或配置文件,提交后由 Cloudflare 重新构建静态页面。后台的关键是把浏览器编辑、鉴权、GitHub API 和构建校验连接起来。

后台页面结构#

管理页面位于 src/pages/admin/,使用独立的 AdminLayout.astro,不加入主站的 Swup 容器。布局加载管理员验证、后台样式、侧栏和命令面板;各页面把数据和操作交给 Svelte Manager,例如文章使用 PostManager.svelte,文章编辑使用 WriteEditor.svelte,访客和 AI 也分别有自己的 Manager。

后台页面不直接持有 GitHub token。AdminLoginGate.svelte 负责取得管理员会话和发送 X-Admin-Token,组件请求统一的 /api/github 或其他同源 Worker 路由。这样 token 不会因为 Svelte 编译而出现在公开 JavaScript 中,后台页面也能在认证失败时统一显示状态。

文章编辑数据#

WriteEditor.svelte 同时处理 Frontmatter 表单、Markdown 正文、预览和本地草稿。真正生成文件内容的逻辑位于 src/utils/adminContent.ts。它不会把整个文档解析成对象后重新序列化,而是定位 Frontmatter 的字段范围,尽可能保留原有注释、换行、字段顺序和正文。

这种处理方式对 Markdown 很重要。重新序列化会改变大量无关行,造成难以审核的提交;局部替换只修改用户真正改动的字段。日期、布尔值、数组和空值仍然需要按 Schema 预期输出,例如 draft: false 必须是 YAML 布尔值,而不是字符串。

发布链路#

text
WriteEditor.svelte
adminContent.ts 生成目标文本
AdminLoginGate / X-Admin-Token
同源 /api/github
src/workers/github-proxy.js
GitHub Contents 或 Git Data API
仓库分支更新
Cloudflare 构建 Astro 页面

代理层先检查来源、管理员授权、请求体大小、目标仓库、分支名、HTTP 方法和路径白名单。允许写入的路径只覆盖配置过的内容、资源和后台文件,拒绝 ..、反斜杠和不安全分支名。生产分支还要经过来源和部署环境保护,避免预览页面把内容写入生产。

并发保护#

单文件写入使用 GitHub Contents API 的文件 SHA;批量内容操作还会先读取分支 HEAD 和仓库 tree。后台提交时带上 expectedHead 和每个文件的 expectedSha,代理发现远程版本已经变化就返回 409,要求编辑器刷新预览后再提交。

这一步避免两个浏览器互相覆盖。按钮上的 loading 状态只能防止同一个页面重复点击,不能判断 GitHub 上是否有人修改过同一个文件。批量提交还会限制文件数量、单文件大小和总请求体大小,并在更新分支引用前再次确认 HEAD 没有变化。

删除和资源写入#

删除文章不是发送空字符串。后台要调用允许的 DELETE 路径,并提供当前文件 SHA;代理仍然执行路径白名单和生产分支保护。图片或书籍封面上传到仓库时,文件扩展名、大小和目标目录也会被验证,避免后台成为任意文件写入接口。

如果新增一种可编辑资源,需要同步四层:Manager 的表单模型、adminContent.ts 的文本修改、github-proxy.js 的路径/方法白名单、scripts/verify-admin/ 的验证脚本。只改 UI 会出现预览正常但提交被拒绝,只改代理又可能产生构建无法解析的内容。

后台与静态构建的关系#

后台发布成功只代表 GitHub 分支已更新,不代表线上页面已经完成构建。Cloudflare 需要拉取新内容、运行 pnpm build、生成静态页面和 Pagefind 索引。发布后应该检查构建状态和实际页面,而不是只根据 API 的 200 响应判断成功。

文章列表为空时,先检查后台写入的目标路径是否位于 src/content/posts/、Frontmatter 是否符合 content.config.ts、是否误设为草稿,再检查部署是否使用了最新构建。后台显示已保存和公开页面出现之间存在构建延迟,这是静态站点的正常边界。

修改后台的检查范围#

修改表单字段:看对应 Manager 和 adminContent.ts;修改登录:看 AdminLoginGate.svelte、Worker 鉴权和管理员限流;修改 GitHub 能力:看 github-proxy.js 的允许路径、方法和分支保护;修改内容格式:看 Schema、Frontmatter 验证和构建脚本。后台页面使用独立布局,涉及导航或 Swup 时不要直接套用主站页面的脚本。

Terminal windowpowershell
# 验证 Frontmatter 保留、后台草稿、taxonomy、认证和代理路径白名单。
pnpm verify:admin
# 检查仓库中是否出现被跟踪的密钥或 token。
pnpm verify:secrets
# 验证发布内容能通过集合、Markdown、图片和静态构建。
pnpm build

管理员验证脚本覆盖 Frontmatter、草稿、taxonomy、例行任务、认证限流和代理规则。远程 GitHub 权限、Cloudflare 环境变量和实际部署仍然需要在目标环境验收。

Contents API 和 Git Data API#

简单的单文件创建、更新和删除可以使用 GitHub Contents API:请求中包含仓库路径、分支、内容和当前文件 SHA。需要同时修改多篇内容时,代理会读取分支 commit、base tree 和 blob,创建新 tree 与 commit,再用 fast-forward 方式更新分支引用。两条路径都必须经过相同的白名单、认证和分支保护,不能因为批量接口走 Git Data API 就绕过保护。

代理还会限制允许的文件扩展名和目录。文章只能写入内容目录,媒体只能写入配置过的资源目录,配置文件只能写入明确列出的文件。路径经过 URL 解码和规范化后再判断,拒绝 ..、反斜杠、空路径和不安全分支名。

草稿、本地预览和发布#

后台本地草稿保存在浏览器状态,不会自动进入仓库;点击发布才会生成 Markdown 文件并提交。预览使用同一套 Frontmatter 和 Markdown 管线,但预览成功不代表远程构建成功,尤其是图片相对路径、MDX import、集合字段和 Worker 环境变量仍可能在构建阶段失败。

如果要增加“定时发布”或“草稿箱”,不能只在 Svelte 中增加一个状态字段。当前架构没有数据库草稿表,真正可靠的方案需要新增存储位置、权限边界和构建策略,否则刷新浏览器就会丢失服务端状态。

Manager 的状态边界#

Manager 组件通常同时维护列表、编辑对象、请求状态和 toast。列表数据来自 GitHub 读取结果,编辑对象是用户尚未提交的本地副本,提交成功后才用返回的 commit 或文件内容刷新列表。不要在输入框绑定到列表原对象,否则取消编辑时无法恢复原值,多个表单也会互相覆盖。

后台的删除、上传和批量操作都应显示明确的进行中状态,并在 401、403、409、413 和 429 时给出不同处理:401 重新登录,403 检查权限,409 刷新远程版本,413 缩小请求,429 等待重试。统一错误处理比在每个按钮里拼接字符串更容易维护。

[ 公告 ]

如果你喜欢,那么欢迎来到我的世界!

了解更多
[ 音乐 ]
封面

音乐

找不到相关结果。
[ 目录 ]
[ 全部文章 ]