从内容建模、渲染管线、后台控制、部署链路到运维策略,系统拆解 Coet 个人工作站的架构边界、工程规范与演进路径,给出一套适用于个人站点长期演化的工程化蓝图。
当前版本:v2.4.x
技术骨架:Next.js 15 App Router、Contentlayer 2、MDX、Drizzle ORM、SQLite
核心目标:内容优先、结构清晰、低成本上线、长期可维护
Coet 并非一个简单的“博客加后台”堆砌产物,而是一套聚焦内容流转、交互沉淀与自动化交付的全栈个人工作站。其核心工程命题在于:当系统需并存纯静态生成、动态交互与长期状态管理时,如何通过克制的架构分层维持项目的心智负担底线。
本文依循“定位 -> 架构 -> 内容 -> 数据 -> 运维”演进推导。所有技术决策皆以单人长期维护可用性为锚点,拒绝任何为了炫技而脱离业务的过度设计。
一、系统边界定义
一言以蔽之:Coet 是以 MDX 内容引擎为基石、以 Feature-based 目录组织为骨架的重内容型独立工作站。
TIP
架构设计的终极奥义是取舍。个人工作站不需要多租户隔离,也不需要分布式高并发,它需要的是极致的单实例稳定性与最低的维护疲劳度。
二、架构拓扑与领域划分
2.1 目录拓扑图
采用极简但高内聚的 Feature-based 架构:
.├── content/ # 📌 静态资产域:MDX 文章与作者信息├── scripts/ # 🛠️ 工程脚本域:构建、部署、SEO 触发├── src/│ ├── app/ # 🚪 路由入口域:RSC 与 Layout 组合│ ├── features/ # 📦 业务能力域:高内聚模块│ │ ├── admin/ # -> 后台管控台│ │ ├── comments/ # -> 评论与反黄牛基建│ │ ├── content/ # -> MDX 解析与渲染管线│ │ └── site/ # -> 全局基础 UI 资产│ ├── server/ # ⚙️ 服务逻辑域:DB 连接与 Drizzle Schema│ └── shared/ # 🧩 共享支撑域:跨限界上下文工具└── storage/ # 💾 运行时状态域:SQLite 与 JSON 配置为什么严禁 app/ 直连数据库?
无论 Server Components 如何鼓吹全栈边界模糊,强行在页面中穿插 SQL 都将导致文件职责极度劣化。app/ 仅负责 URL 路由与数据组合,真正的查询操作必须拦截在 server/ 或对应 features/ 的数据服务中。
三、内容引擎:MDX 降容与升维
3.1 渲染管线切片
通过 Contentlayer 接管文件系统监听,配合 unified 生态体系构建重型解析管线:
mdx: { remarkPlugins: [ remarkExtractFrontmatter, remarkGfm, remarkCodeTitles, // 支持代码块带文件名 remarkAlert, // 支持 GitHub Blockquote Alerts remarkCustomDirectives// 支持 :::info 等自定义指令 ], rehypePlugins: [ rehypeRemoveFirstH1, rehypeOptimization, // 注入 img 防盗链与 a 标签外链隔绝 rehypeSlug, [rehypePrettyCode, rehypePrettyCodeOptions], ],}3.2 节点劫持与组件投射
标准的 MD 元素在渲染侧被强制劫持替换,注入更高阶的前端特性:
img=> 被剥夺原生标签身份,替以集成referrerPolicy="no-referrer"、首屏懒加载与基于 Next.js 的图片优化组件MdxImage。a=> 如果是外链,则强制挂载target="_blank" rel="noopener noreferrer"以物理阻断跨站脚本污染。pre=> 包裹为带有复制按钮和语言标识的CodeBlockPre交互容器。
在这套生态下,MDX 不再是“文字数据”,而是通过 AST 编译得到的 Server Component 集。你插入的一个组件可能引发一次服务端查库,但其最终回传给浏览器的,只是被极致压缩的纯 HTML。
四、数据与运行时持久化
4.1 运行时安全区定义
WARNING
一切运行时生成的数据必须被软隔离。这就要求系统必须建立坚不可摧的 storage/ 边界。
任何属于配置层面(例如页面元数据、广告位图、用户层级友链)或操作产生的数据(评论记录、点赞)全部导入至 ./storage 隔离区。
// 核心评论控制表,直接依托本地 SQLite 建立高效索引export const comments = sqliteTable('comments', { id: integer('id').primaryKey({ autoIncrement: true }), postId: text('post_id').notNull(), content: text('content').notNull(), status: text('status', { enum: ['pending', 'approved'] }).default('pending'), createdAt: integer('created_at', { mode: 'timestamp' }).default(sql`CURRENT_TIMESTAMP`),});4.2 数据流向管控
- 配置状态(JSON) => 负责极低频的字典数据,如全站设定,变更后通过后台触发重现管线。
- 业务持久化(SQLite/Drizzle) => 满足结构化查询所需,解决评论嵌套树等强关联性诉求。
五、发布体系:面向失败编程
5.1 原子化守护部署
一套合格的个人站点无需繁重的 CI/CD,但必须实现极端的环境一致性保护:
- 环境检查段:强制侦测关键的运行常量
.env是否有效装载。 - 基建组装段:执行
pnpm install。 - 数据预处理段:处理 sqlite 的 schema 同步构建。
- 内核重编译段:生成
search.json,完成前端资产包的构建。 - 守护重组段:
pm2 reload实现平滑切换,断点接管进程。 - 健康收尾段:本地 127.0.0.1 发送健康拨测,成功后挂钩外网搜索引擎主动爬虫推送
seo:push。
哪怕只是最简单的 shell 交付通道,只要它切断了手动在线上跑命令的惯性,就彻底阻绝了“为什么本地能跑线上跑挂了”的薛定谔代码。
六、结语
Coet 工作站的生命力并非源自任何时髦库的堆砌,而是来自于一种强迫症般的工程洁癖:明确前后端数据的分水岭、掌控每一次 AST 的底层编译转化、极度节约运算资源的渲染策略。
它解决了作为程序员独狼战场的终极矛盾:在自由挥洒技术理想的同时,不用陷入到日复一日修补烂代码的泥潭之中。1
Footnotes
对待这类项目,写更少的代码,建更硬的墙,是最冷酷但也最有效的信条。 ↩
