整体介绍
博客的整体分为以下三部分:
- 博客后台,使用express + mysql,提供接口
- 博客管理端,使用vue3 + ts + vite,提供博客和分类增删改查的功能
- 博客前端,使用vue3 + ts + vite,负责展示博客内容
博客地址:影月的个人博客
页面展示如下:
博客后台
集成数据库
1. 本地安装数据库
# 安装
brew install mysql
# 启动数据库
brew services start mysql
# 登陆数据库
mysql -u root -p
2. 建表
目前博客仅有三张表,用户表、分类表、文章表
-- 创建用户表
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
-- 用户ID,主键,自增
user_name VARCHAR(50) NOT NULL UNIQUE,
-- 用户名,非空且唯一
user_password VARCHAR(255) NOT NULL,
-- 密码,非空
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
desc articles;
-- 创建分类表
CREATE TABLE category (
id INT AUTO_INCREMENT PRIMARY KEY,
category_name VARCHAR(50) NOT NULL UNIQUE,
category_desc VARCHAR(100) NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 创建文章表
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
-- 唯一标识,每个博客文章的自增 ID
title VARCHAR(255) NOT NULL,
-- 文章标题,最大长度为 255 字符
content MEDIUMTEXT NOT NULL,
-- 文章内容,存储中大型文本
article_desc VARCHAR(100),
author VARCHAR(100),
-- 作者名称,最大长度为 100 字符
category_id INT,
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- 文章创建时间,默认值为当前时间
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- 文章更新时间
view_count INT DEFAULT 0 -- 浏览次数,初始为 0
);
3. express连接数据库
express中使用mysql2连接数据库
pnpm install mysql2
添加数据库配置
const mysql = require("mysql2/promise");
// 需要把这里换成真实的服务器信息
// 设置MySQL连接池
const pool = mysql.createPool({
host: "searverIp",
port: 3306,
user: "root",
password: "xxxxxx",
database: "xxxxx",
waitForConnections: true,
connectionLimit: 10, // 连接池中允许的最大连接数
queueLimit: 0, // 队列中允许的最大请求数,0表示无限制
});
module.exports = pool;
在接口中访问数据库
var express = require("express");
const pool = require("../config/dbConfig");
var router = express.Router();
router.get("/list", async (req, res) => {
let connection;
try {
// 从连接池中获取一个连接
connection = await pool.getConnection();
const sqlQuery = `SELECT * FROM category`;
const [rows, fields] = await connection.execute(sqlQuery);
res.status(200).json({
code: 200,
msg: "查询成功",
data: transformArray(rows),
});
} catch (err) {
// 处理错误
console.error("Error executing query:", err);
res.status(500).json({ error: "Internal Server Error" });
} finally {
// 确保连接被释放回连接池(如果它仍然存在)
if (connection) {
connection.release();
}
}
});
express中的路由
目前博客分为用户/分类/文章三块路由,在router目录下实现用户/文章/分类的增删改查
const express = require("express");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const categoryRouter = require("./router/category");
const articlesRouter = require("./router/articles");
const userRouter = require("./router/user");
const app = express();
const port = 4000;
// 使用 cors 中间件
app.use(cors());
// 中间件,解析 JSON 请求体
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 解析cookie
app.use(cookieParser());
app.use("/category", categoryRouter);
app.use("/article", articlesRouter);
app.use("/user", userRouter);
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
登陆校验
- 用户登陆,后台使用jsonwebtoken生成token,把token返回在cookie中
- 在需要鉴权的接口中校验token,校验成功进入正常逻辑,校验失败返回code=700
- 客户端判断code=700,表示token失效,跳转到700
1. 使用jsonwebtoken生成token
utils/index.js
const jwt = require("jsonwebtoken");
const jwtSecret = "blog_admin";
function generateToken(userId) {
const token = jwt.sign({ userId }, jwtSecret, { expiresIn: "1d" });
return token;
}
2. 登陆接口,在cookie中添加token
router/user.js
const [rows] = await connection.execute(
`SELECT * FROM users WHERE user_name = '${username}'`
);
const user = rows[0];
const token = generateToken(user["user_id"]);
res.cookie("token", token, { httpOnly: true });
3. 增加token校验中间件
middleware/jwt.js
const jwt = require("jsonwebtoken");
const jwtSecret = "blog_admin";
function verifyToken(req, res, next) {
const token = req.cookies.token;
if (!token) {
return res.json({
code: 700,
msg: "无效的token",
});
}
jwt.verify(token, jwtSecret, (err, userId) => {
if (err) {
return res.json({
code: 700,
msg: "无效的token",
});
}
req.userId = userId;
next(); // 调用next()来继续处理请求
});
}
4. 在路由中使用校验中间件
例如只有具有token的用户,才能进行删除操作
router.post("/delete", verifyToken, async (req, res) => {
// ...
}
服务器上连接数据库
在服务器上安装mysql,参考在Linux实例中安装MySQL数据库,登陆、建表过程如上节,不再赘述。
express应用部署在容器中,mysql部署在宿主机上,可能会导致连接不上,需要开放mysql被连接的权限
-
mysql默认只能被自身ip连接,需要开放被外部ip连接,在/etc/my.cnf文件上修改 [mysqld] bind-address = 0.0.0.0
-
授予特定用户从指定ip访问数据库的权限 # 如果没有用户,需要先创建用户 CREATE USER 'app_user'@'your_container_ip' IDENTIFIED BY '@Hmh6861860@'; # 授予这个用户对数据任意操作的权限,登陆mysql后操作,在>mysql>下进行 GRANT ALL PRIVILEGES ON your_database.* TO 'app_user'@'your_container_ip'; FLUSH PRIVILEGES;
-
开放安全组 允许容器ip访问3306端口
博客前端
博客管理端
- 包含登录、文章管理、分类管理功能,进行修改/删除等操作前需要校验token,接口返回700跳转登陆页;
- 安全组仅对自身ip开放即可。
博客前端
- 使用md-editor-v3完成文章的预览
其他内容在之前的文章中都有记录,在此就不作赘述。