blog
blog
发布于 2026-04-13
博客系统
基于 Node.js + Express + Nunjucks + MySQL + TailwindCSS 的博客系统,支持前台浏览与后台管理,以及模块化CMS功能。
环境要求
- Node.js >= 18 LTS
- MySQL 5.7+ / 8.x
- npm
安装与运行
1. 安装依赖
cd src
npm install
2. 配置环境变量
cp .env.example .env
# 编辑 .env,填写数据库连接信息、JWT 密钥等
主要环境变量说明:
| 变量 | 说明 | 默认值 |
|---|---|---|
PORT |
服务端口 | 3300 |
DB_HOST |
MySQL 主机地址 | 117.72.220.18 |
DB_PORT |
MySQL 端口 | 3306 |
DB_USER |
数据库用户名 | - |
DB_PASSWORD |
数据库密码 | - |
DB_NAME |
数据库名 | blog |
JWT_SECRET |
JWT 签名密钥(>=32位) | - |
BUILD_MODE |
构建模式:ssr 或 static |
ssr |
SITE_NAME |
站点名称 | 我的博客 |
3. 初始化数据库
npm run init-db
这会创建数据库表结构并插入默认管理员账号:admin / admin123。
如需启用模块CMS功能,执行迁移脚本:
mysql -h <host> -u <user> -p <database> < src/scripts/migrations/v2.0.0_module_cms.sql
4. 构建样式
npm run build:css
5. 启动服务
# 开发模式(热重载)
npm run dev
# 生产模式
npm start
一键启动
chmod +x run.sh
./run.sh
目录说明
src/
├── app.js # Express 应用入口
├── config/ # 配置文件
│ ├── index.js # 统一配置(读取 .env)
│ ├── db.js # MySQL 连接池
│ └── swagger.js # Swagger API 文档配置
├── middleware/
│ └── auth.js # JWT 鉴权中间件
├── routes/
│ ├── api/
│ │ ├── auth.js # POST /api/login
│ │ ├── articles.js # /api/articles CRUD
│ │ └── categories.js # /api/categories CRUD
│ └── pages.js # 前后台页面路由
├── views/ # Nunjucks 模板
│ ├── layouts/ # 布局模板(前台 base / 后台 admin)
│ ├── index.njk # 首页/文章列表
│ ├── article.njk # 文章详情
│ ├── category.njk # 分类文章列表
│ ├── admin/ # 后台管理页面
│ └── ...
├── public/ # 静态资源
│ ├── css/ # TailwindCSS 样式
│ └── js/ # 前端脚本
├── scripts/
│ ├── init.sql # 建表 SQL
│ ├── init-db.js # 数据库初始化脚本
│ └── build.js # 静态站点构建脚本
└── .env.example # 环境变量模板
访问地址
| 地址 | 说明 |
|---|---|
http://localhost:3300 |
前台首页 |
http://localhost:3300/admin/login |
后台登录 |
http://localhost:3300/api-docs |
Swagger API 文档 |
构建静态站点
npm run build
构建产物输出到 src/dist/ 目录,可部署到任意静态服务器(nginx 等)。
前台渲染架构(双轨机制)
系统存在两条并行的前台渲染链路,通过 views/layouts/base.njk 中的 hasPageModules 标志自动切换:
请求进入 → systemModules 中间件查 pages 表匹配 path
→ 有匹配 + 有模块配置 → 链路B:模块组合渲染
→ 无匹配 / 无模块配置 → 链路A:硬编码路由 + views/*.njk
链路A — 传统模板渲染(旧系统)
- 入口:
routes/pages.js硬编码路由(/、/articles/:id、/categories/:id、/about) - 每个路由手动查数据库,调用
res.render('xxx.njk')渲染对应views/*.njk模板 - 模板继承
views/layouts/base.njk,通过{% block content %}填充页面内容 - 适用于:未配置模块的页面、后台管理页面
链路B — 模块组合渲染(CMS 系统)
- 入口:
middleware/systemModules.js,在所有页面路由之前执行 - 按
pages.path匹配当前 URL(支持精确匹配和动态路由如/article/:id) - 查询
page_module_configs获取该页面绑定的模块(按激活的站点分类过滤) - 用 Nunjucks 逐个渲染模块模板,拼接为
allModulesHtml注入到base.njk - 当
hasPageModules = true时,base.njk直接输出模块拼接结果,跳过{% block content %}
交汇点 — views/layouts/base.njk
{% if hasPageModules %}
{{ allModulesHtml | safe }} {# 链路B:整页由模块拼出 #}
{% else %}
<header>...</header>
<main>{% block content %}{% endblock %}</main> {# 链路A:传统模板 #}
<footer>...</footer>
{% endif %}
关键结论:
- 后台"新建页面"创建的是
pages表记录(链路B),不会生成.njk文件 - 页面配了模块就走 CMS 渲染,没配就自然回退到传统模板
views/*.njk文件只在链路A中使用,链路B完全由数据库驱动
模块化CMS系统
系统支持灵活的页面模块管理功能,详细文档请查看:MODULE_CMS.md
四层架构:
module_types (模块类型: header/nav/section/footer)
└── module_templates (模板: 每种类型可有多个模板变体)
└── module_instances (实例: 绑定模板 + 具体配置数据)
└── page_module_configs (页面配置: 实例挂载到页面 + 排序 + 站点分类)
站点分类与多配置方案(v2.4.0):
- 页面路径全局唯一,但同一页面可按站点分类拥有多套模块配置
site_category_id在page_module_configs上,不在pages上- 前台渲染时按当前激活的站点分类过滤模块配置
- 后台可切换分类查看/编辑不同配置方案
核心功能:
- 模块类型管理(页头、导航、内容区、页脚)
- 模块模板管理(HTML + CSS + JSON Schema)
- 模块实例管理(动态配置 + 自定义样式)
- 保存实例为新模板
- Nunjucks渲染引擎 + 缓存优化
技术栈:
- 后端: ajv + ajv-formats(JSON Schema验证)
- 前端: React 19 + @rjsf/core(动态表单)+ Monaco Editor
- 缓存: node-cache(5秒TTL)
验收标准对应
| 模块 | 验收编号 | 入口 |
|---|---|---|
| 登录鉴权 | AC-001 ~ AC-003 | routes/api/auth.js + middleware/auth.js |
| 文章管理 | AC-010 ~ AC-016 | routes/api/articles.js |
| 回收站 | AC-020 ~ AC-022 | routes/api/articles.js(DELETE/restore) |
| 分类管理 | AC-030 ~ AC-034 | routes/api/categories.js |
| 配置管理 | AC-040 ~ AC-042 | config/index.js + .env |
| 静态构建 | AC-043 ~ AC-044 | scripts/build.js |
| API 文档 | AC-050 ~ AC-051 | config/swagger.js + Swagger JSDoc |
| 模块CMS | v2.0.0 | routes/api/module-*.js + lib/module-renderer.js |
数据库迁移可用命令
npm run migrate:status # 查看迁移状态 npm run migrate:up # 应用所有待执行迁移 npm run migrate:down # 回退最后一个迁移 npm run migrate:reset # 回退所有迁移