为什么重新做一遍
个人站点常见的两种状态:一种是静态模板,改一篇文章要重新构建;另一种是功能堆得很满,但每个部分都只完成了一半。
这个项目的目标很明确:前台负责展示,后台负责维护,任何一篇文章都不需要改代码。同时控制复杂度,单人博客不需要微服务、消息队列或复杂缓存。
技术选型
| 层次 | 选择 | 原因 |
|---|---|---|
| 框架 | Next.js App Router | 服务端渲染、路由和 API 集中在一处 |
| 语言 | TypeScript | 数据边界更清晰 |
| 数据库 | SQLite | 单文件、零运维,适合个人规模 |
| 样式 | Tailwind CSS + 自定义设计令牌 | 保持统一,不引入整套组件库 |
选型的原则不是“看起来先进”,而是每个依赖都能回答一个问题:它解决了什么当前真实存在的问题?
数据模型
文章、分类、标签、项目分别建表,文章与标签通过关联表连接:
CREATE TABLE post_tags (
post_id TEXT NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
tag_id TEXT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (post_id, tag_id)
);
删除文章时由外键级联清理关联;更新标签之后再清理没有文章引用的标签,避免留下无法管理的孤立数据。
认证与安全
后台没有把“路径不公开”当作安全措施。所有管理 API 都在服务端检查会话、请求来源和 CSRF 令牌:
export async function requireAdminApi(request: NextRequest) {
assertSameOrigin(request);
const auth = await authenticateRequest(request);
if (request.method !== "GET") assertCsrf(request, auth);
return auth;
}
会话令牌只以 SHA-256 哈希形式写入数据库,Cookie 使用 HttpOnly 和 SameSite=Lax,生产环境自动启用 Secure。登录失败按 IP 和账号分别计数,超过阈值后短暂锁定。
上线前还要做什么
- 文章 CRUD、发布与下架
- Markdown 渲染、代码高亮、文章目录
- 图片对象存储(当前保存在本地目录)
- 访问统计(在没有真实数据前不做展示)
本文是初始化时的示例内容,可在后台直接编辑、下架或删除。
一次完整的请求会经过什么
以文章详情页为例:
- 路由匹配
/blog/[slug]; - 服务端向 SQLite 查询已发布且到达发布时间的文章;
- Markdown 经过统一管线渲染为 HTML,同时抽取目录标题;
- 页面输出结构化数据、Open Graph 元信息和阅读进度组件。
整个过程没有客户端数据请求,首屏就是完整内容,对搜索引擎和分享预览都更友好。