一个开箱即用、可自托管的开源个人博客框架。基于 Next.js 15 App Router,文章存数据库、后台可视化写作、内容可一键导出为标准 Markdown——你随时可以带走全部内容。
git clone <your-fork-url> my-blog && cd my-blog
cp .env.example .env # 改 DATABASE_URL / AUTH_SECRET / ADMIN_PASSWORD
npm install
npm run db:push # 建表(SQLite 零配置)
npm run db:seed # 可选:写入示例文章
npm run dev # http://localhost:3000后台入口在页脚版权行的「管理」链接,或直达 /admin/login。
| 说明 | |
|---|---|
| 数据库为单一内容源 | 文章、分类、标签、评论、订阅者、访问日志全在数据库里。写作不需要 commit,换设备不需要 clone。 |
| 内容不被锁定 | 后台一键导出全站为带 YAML frontmatter 的 Markdown,Hugo / Jekyll / Hexo 可直接读取;同样支持批量导入。 |
| 零第三方依赖的互动 | 评论、点赞、收藏、订阅、阅读量全部自建,不依赖 Giscus / Disqus / 任何 SaaS。 |
| 一个配置文件 | 站点名称、导航、作者、赞赏位、功能开关全部集中在根目录 site.config.ts,改完即热更新。 |
| LLM 友好 | 内置 /llms.txt 与 /llms-full.txt,遵循 llmstxt.org 规范。 |
| 可观测 | 后台「访问管理」页查看访问日志、按 IP 封禁,封禁在 middleware 层生效。 |
Next.js 15(App Router · RSC)· React 19 · TypeScript · Tailwind CSS 4 · Prisma · Auth.js v5 · MDX(shiki 高亮)· KaTeX · FlexSearch · Zod · Vitest
Node.js ≥ 20 · npm ≥ 10
npm install
cp .env.example .env必填项:
| 变量 | 说明 |
|---|---|
DATABASE_URL |
本地默认 file:./dev.db(SQLite,零配置)。生产改用 PostgreSQL 连接串,并同步把 prisma/schema.prisma 的 provider 改为 postgresql。 |
AUTH_SECRET |
Auth.js 会话密钥,openssl rand -base64 32 生成。 |
ADMIN_EMAIL / ADMIN_PASSWORD |
后台登录凭证。密码支持明文或 bcrypt 哈希($2 开头)。 |
可选项(不配则对应功能自动降级,不会报错):
| 变量 | 作用 |
|---|---|
NEXT_PUBLIC_SITE_URL |
站点根地址。生产必须设置,影响 RSS / Sitemap / OG 图。 |
NEXT_PUBLIC_SITE_NAME、NEXT_PUBLIC_SITE_DESCRIPTION |
覆盖 site.config.ts 中的同名字段。 |
SMTP_HOST/PORT/USER/PASS/FROM |
邮件通知(订阅确认 / 评论通知 / 新文章推送)。缺任一则不发信。 |
MEDIA_LOCAL_DIR |
媒体本地存储目录。留空走默认 <项目根>/data/uploads,Docker 下与 SQLite 共享 ./data 卷 |
完整列表见 .env.example。环境变量在 lib/env.ts 用 Zod 惰性校验,缺失时给出可执行的修复提示,而不是让程序在运行时行为异常。
npm run db:push # 开发:直接把 schema 推到数据库
npm run db:seed # 可选:写入 4 篇示例文章生产环境建议改用迁移:npm run db:migrate。
npm run dev编辑根目录 site.config.ts——这是整个框架里唯一需要你改的文件:站点名称、短名、描述、语言、作者资料、顶部导航、每页条数、赞赏二维码、功能开关。
功能开关(features)关闭后,对应模块在前台完全不渲染,不留下空的 UI 骨架:
features: {
comments: true, // 评论 + 楼中楼回复
subscribe: true, // 邮件订阅
reactions: true, // 点赞 / 收藏
search: true, // 全文搜索
rss: true, // /rss.xml
llmsTxt: true, // /llms.txt 与 /llms-full.txt
accessLog: true, // 访问日志(高流量站点可关闭以减轻数据库压力)
}头像、赞赏码等静态资源放在 public/ 下。
内容:Markdown/MDX 写作(GFM、数学公式 KaTeX、代码高亮 shiki、callout 指令)、草稿/发布、定时发布、分类、标签、归档、全文搜索(中文 bigram 分词)、相关文章、上下篇导航、阅读时长、目录(TOC)。
互动:评论与楼中楼回复、后台审核、点赞、收藏、邮件订阅(含确认与退订)。
后台:文章增删改查、实时预览(编辑/预览/分屏三模式)、Markdown 导入导出、草稿分享链接(带随机令牌,可轮换撤销)、友链管理、语录管理、订阅者管理、访问日志与 IP 封禁。
SEO / 分发:每篇文章动态生成 OG 图、sitemap.xml、robots.txt、RSS、llms.txt / llms-full.txt、规范链接、结构化元信息。草稿预览页自动标记 noindex。
后台「文章」页的工具栏提供两个按钮:
- 导出:全站文章打包成单个
.md备份文件(<!-- blog-export: slug -->分隔),可直接喂给任何静态博客生成器。 - 导入:上传
.md或.zip,按 slug 覆盖或跳过,返回逐篇的导入结果与告警。
底层实现在 lib/post-io.ts,往返转换有完整测试覆盖。
| 命令 | 说明 |
|---|---|
npm run dev |
开发服务器 |
npm run build / npm start |
生产构建 / 启动 |
npm run lint |
ESLint |
npm run typecheck |
tsc --noEmit |
npm test |
Vitest 单元测试(135 项,覆盖内容转换/搜索/限流/反垃圾/版本快照/配置等纯函数) |
npm run lighthouse |
桌面+移动 × 首页+文章页 性能审计(需先 npm run build && npm run start) |
npm run test:watch |
测试 watch 模式 |
npm run db:push |
把 schema 推到数据库(开发) |
npm run db:migrate |
创建并应用迁移(生产) |
npm run db:generate |
重新生成 Prisma Client |
npm run db:seed |
写入示例数据 |
.
├── site.config.ts # 站点唯一配置文件(改这里)
├── app/
│ ├── (前台页面) # 首页 / 博客 / 分类 / 标签 / 归档 / 搜索 / 关于 / 友链
│ ├── admin/ # 后台:文章、评论、订阅者、友链、语录、访问管理
│ ├── api/ # 评论、点赞、收藏、订阅、浏览量、导入导出、分享令牌、安全校验、健康检查
│ ├── preview/[slug]/ # 草稿预览(令牌访问,noindex)
│ ├── rss.xml/ llms.txt/ llms-full.txt/
│ ├── sitemap.ts robots.ts
│ └── blog/[id]/opengraph-image.tsx # 动态 OG 图(路径用 cuid id 锚定;旧 /blog/[slug] 自动 308 重定向)
├── components/ # UI 组件(含 admin/ 后台组件)
├── lib/ # 领域逻辑
│ ├── content.ts # 文章查询层
│ ├── post-io.ts # Markdown 导入导出
│ ├── search.ts # FlexSearch + 中文分词
│ ├── env.ts # Zod 环境变量校验
│ ├── rate-limit.ts # 滑动窗口限流
│ ├── mail.ts # SMTP 邮件
│ └── site-config.ts # 配置聚合(不要直接改,改 site.config.ts)
├── prisma/
│ ├── schema.prisma # 数据模型(SQLite / PostgreSQL 双支持)
│ └── seed.ts
├── tests/ # Vitest 单元测试
└── docs/ # 设计文档
- Docker:
docker compose up -d --build(默认内置 SQLite,数据持久化到./data;切换到 PostgreSQL 的方法见docker-compose.yml注释)。 - Vercel / Node 平台:设置环境变量后
npm run build && npm start。
部署前务必设置 NEXT_PUBLIC_SITE_URL 为真实域名,否则 RSS、Sitemap、OG 图里的链接会指向 localhost。完整步骤见 DEPLOY.md。
⚠️ 开发期间不要运行npm run build——构建与 dev server 共用.next目录,会导致缓存损坏。开发期用npm run typecheck验证;全量构建请先停掉 dev server。
- 后台由 middleware 守卫(Auth.js),未登录访问
/admin/*跳转登录页。 - 登录失败 5 次锁定 15 分钟。
- 评论、订阅等写接口按 IP 滑动窗口限流(
lib/rate-limit.ts,单实例内存实现;多实例部署请替换为 Redis)。 - IP 封禁在 middleware 层生效,命中直接返回 403。
- 密码支持 bcrypt 哈希存储。
- 限流与登录锁定为单实例内存实现,多副本部署需换成 Redis 等共享存储。
- 图片使用原生
<img>(关闭了next/image优化),以规避远程图源白名单配置。 - 文章正文以 Markdown 存入数据库,未做版本历史。
欢迎提交 Issue 与 PR,请先阅读 CONTRIBUTING.md。提交代码前请确保 npm run typecheck && npm test && npm run lint 全部通过。