一篇文章被发布,通常意味着打开后台、找到编辑器、粘贴正文、上传封面、填写分类,再点击一次发布。

但你现在读到的这篇文章不是这样出现的。它的封面先通过短期凭证直传七牛云,正文随后以 JSON 交给网站,服务端完成 Markdown 渲染、阅读时长计算、标签关联和发布。整个过程没有打开文章编辑页面。

内容从创作端流向 API、云存储与网站终端

这不是为了少点几次鼠标,而是让个人网站从一个“只能进入后台操作的成品”,变成一个可以被其他工具调用的创作系统。

后台页面只有一扇门

传统 CMS 的能力往往被锁在管理界面里。发布文章只能用浏览器,上传图片只能点击按钮,更新作品只能打开表单。只要离开这个页面,能力就不存在。

问题会在创作链路变长时显现出来:

  • 笔记写在 Obsidian,却要手动复制到网站;
  • 图片已经在本地整理好,仍要逐张选择上传;
  • 作品信息同时存在于项目文档和网站,修改时要维护两份;
  • 想做定时发布、批量迁移或自动备份,只能重新模拟人工操作。

一个稳定的 API 相当于为网站增加第二扇门。后台仍然适合人工编辑,而脚本、桌面工具和自动化服务可以走一条结构清晰、可验证的通道。

抽取能力,而不是复制一套后台

最容易犯的错误,是给文章创建一个接口,却在接口控制器里重新写一遍保存逻辑。这样做短期很快,长期一定分叉:后台生成了阅读时长,API 忘了;后台能关联标签,API 保存成了纯文本;后台更新作品媒体,API 留下旧记录。

真正需要抽取的是“发布文章”这件事本身:

输入数据
  ├─ 校验标题、分类与状态
  ├─ 生成唯一 slug
  ├─ 渲染 Markdown
  ├─ 计算阅读时长
  ├─ 同步标签与媒体
  └─ 在事务中保存

后台表单和 API 只是两个入口,它们应该调用同一份内容服务。这样,无论文章来自浏览器、命令行还是未来的写作客户端,最终规则都一致。

令牌必须比密码更克制

让程序访问网站,不应该把管理员密码交给脚本。个人访问令牌解决的是“谁可以做什么,以及可以做多久”。

例如,一枚用于写作同步的令牌只需要:

articles.manage
article_categories.manage
tags.manage
uploads.create

它不需要管理用户,也不应该读取系统设置。即使令牌意外泄露,影响范围仍被限制在内容发布。服务端只保存令牌的 SHA-256 哈希,明文只在创建时展示一次;不用时可以撤销,彻底不用时可以永久删除。

权限不是为了把界面做复杂,而是让自动化拥有清晰边界。

图片直传,让服务器只负责签字

媒体上传是内容 API 里最容易拖垮服务器的部分。更合适的流程是两步直传:

  1. 客户端向网站申请一个短期上传凭证;
  2. 客户端拿着凭证直接把文件传到七牛云;
  3. 上传成功后,把最终 URL 写入文章或作品。

网站服务器只负责验证令牌、限定文件类型、生成存储路径并签发凭证,不承担大文件中转。这既减少带宽压力,也让网页、桌面应用和命令行共享同一套上传方式。

一个真实的发布请求

这篇文章的核心发布动作可以压缩成一次请求:

curl -X POST "https://www.aora.top/api/v1/articles" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "当个人网站有了 API:把创作从后台页面解放出来",
    "content": "Markdown 正文",
    "category_id": 1,
    "tags": ["API", "自动化", "个人网站"],
    "status": 1
  }'

请求成功后,网站返回文章 ID、slug、渲染结果和发布时间。调用方不需要理解数据库结构,也不应该直接接触数据库。

接下来可以真正自动化什么

接口存在之后,真正有价值的不是“能用 curl 发文章”,而是把它接入日常工具:

  • 在 Obsidian 中完成文章后,一条命令同步封面、正文和标签;
  • 将作品目录中的图片按顺序上传,并自动生成作品媒体列表;
  • 定期导出文章、作品和设置,形成可迁移的内容备份;
  • 从其他平台迁移历史文章,同时保留发布日期和原始 slug;
  • 给未来的桌面写作工具或移动端应用提供稳定入口。

个人网站不必变成庞大的平台。它只需要把已有能力整理成边界明确的接口,就能在保持独立的同时,与自己的工具链连接起来。

这篇文章本身就是第一次验证:封面被生成并上传,正文通过接口发布,分类与标签由服务端统一处理。比“接口可以工作”更重要的是,这条路径已经足够清楚,可以被下一段程序重复使用。