文章管理迁移计划:全文内容入库

507 字
2 分钟
文章管理迁移计划:全文内容入库

文章管理迁移计划:全文内容入库#

背景#

当前架构中,文章正文存在 .md 文件里,元数据存在 PostgreSQL 的 articles 表里。每次编辑文章需要同时写文件系统和数据库,且 SSR 渲染时需要通过 astro:content 从文件系统读取正文。

目标:将文章正文也存入数据库,彻底消除文件系统和数据库的同步问题。


表结构设计#

现有表(不变)#

src/db/schema.ts
export const articles = pgTable("articles", {
id: serial("id").primaryKey(),
slug: text("slug").notNull().unique(),
title: text("title").notNull().default(""),
description: text("description").notNull().default(""),
category: text("category").notNull().default(""),
tags: text("tags").array().notNull().default([]),
coverImage: text("cover_image").notNull().default(""),
status: articleStatus("status").notNull().default("published"),
pinned: boolean("pinned").notNull().default(false),
wordCount: integer("word_count").notNull().default(0),
publishedAt: timestamp("published_at"),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});

新增表#

export const articlesContent = pgTable("articles_content", {
id: serial("id").primaryKey(),
articleId: integer("article_id")
.notNull()
.references(() => articles.id, { onDelete: "cascade" }),
content: text("content").notNull().default(""),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
});

分阶段文件变更#

阶段一:数据库层#

文件 操作
src/db/schema.ts 新增 articlesContent
scripts/migrate-content-to-db.ts 新建:读取所有 .md 文件,解析 body,写入 articles_content
drizzle.config.ts 不变

阶段二:Markdown 渲染管道#

将文章正文从 DB 渲染为 HTML,脱离 astro:content.render()

文件 操作
src/utils/markdown-ssr.ts 新建:基于 unified + 现有插件链(14 个 remark/rehype)的独立渲染函数
astro.config.mjs markdown.processor 中的插件配置提取为可共享变量

阶段三:SSR 页面改造#

文件 操作
src/pages/posts/[...slug].astro getSortedPosts() + render(entry) 改为 查 DB + markdown-ssr.ts 渲染
src/pages/api/admin/articles/[id].ts GET 返回 articles JOIN articles_contentPUT 同时写两张表
src/pages/api/admin/articles/index.ts GET 列表不查 content 表(窄表优化)

阶段四:内容工具重写#

文件 操作
src/utils/content-utils.ts getSortedPosts() 直接从 DB 查询,不再调 astro:content;相应调整排序/过滤/标签/分类函数
src/utils/lqip-utils.ts 如果依赖 .md 文件路径,需适配
src/content/posts/ .md 文件可保留作备份,但不再是渲染的数据源

阶段五:清理#

文件 操作
scripts/sync-posts-to-db.ts 删除(不再需要)
.github/workflows/ci.yml 删除 pnpm tsx scripts/sync-posts-to-db.ts 步骤
src/content.config.ts posts collection 可保留或删除

数据流对比#

当前#

管理员保存 → 写 .md文件 + UPDATE articles
新增文章 → 创建 .md文件 + INSERT articles
读者查看文章 → SSR → getCollection → 读 .md文件 → render(entry)
列表页/首页 → getSortedPosts() → getCollection → 读所有 .md → .find()
首次部署 → sync-posts-to-db.ts 从 .md 导入 DB

迁移后#

管理员保存 → UPDATE articles + UPSERT articles_content
新增文章 → INSERT articles + INSERT articles_content
读者查看文章 → SSR → SELECT articles JOIN articles_content WHERE slug=xxx
→ markdown-ssr.ts 渲染 → 返回 HTML
列表页/首页 → content-utils.ts 直接 SELECT articles(不查 content 表)
首次部署 → migrate-content-to-db.ts 一次性迁移后不再需要

markdown-ssr.ts 设计要点#

import { unified } from "unified";
// 复用 astro.config.mjs 中已有的 remark/rehype 插件
// 包括:remarkMath, remarkPlantuml, remarkMermaid, rehypeCallouts,
// rehypeKatex, rehypeSlug, rehypeFigure, ...
export async function renderMarkdown(content: string): Promise<string> {
const file = await unified()
.use(/* 所有现有 remark 插件 */)
.use(/* 所有现有 rehype 插件 */)
.process(content);
return String(file);
}

关键:复用 astro.config.mjs 中已配置的 14 个插件,保持渲染结果与当前一致。


迁移脚本 migrate-content-to-db.ts#

// 遍历 src/content/posts/ 下所有 .md 文件
// 解析 frontmatter → upsert articles 表
// 解析 body → upsert articles_content 表
// 只跑一次,完成后正文数据就在 DB 里了

注意事项#

  1. 删除 schema.ts 里的字段不会自动删数据库——只有跑 drizzle-kit push 才会变更真实数据库
  2. articles_content.article_id 设置了 ON DELETE CASCADE——删除文章时正文自动跟着删
  3. drizzle-kit push 不会删现有的 articles——只会在基础上加新表 articles_content
  4. .md 文件保留不动——作为可选的备份来源,不影响功能
  5. astro:contentrender() 不再用于文章——但其他功能(spec 页面、MDX)仍然可用

执行顺序#

Phase 1: schema.ts + drizzle-kit push + migrate ← 先做这个
Phase 2: markdown-ssr.ts ← 再做这个
Phase 3: posts/[...slug].astro API 端点改造 ← 然后改渲染
Phase 4: content-utils.ts 重写 ← 然后改工具
Phase 5: 清理 sync 脚本 + CI/CD ← 最后

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

文章管理迁移计划:全文内容入库
http://tianyi.ink/posts/cv3/
作者
蓝厘榨只因
发布于
2026-06-18
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
蓝厘榨只因
去码头整点薯条🐦
公告
( 」゚Д゚)」<花生饮料矿泉水,有需要的看一下了啊!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
3
分类
2
标签
13
总字数
1,634
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.12.1
文章许可
CC BY-NC-SA 4.0

文章目录

管理员登录