博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Node项目实战开发-博客系统
阅读量:2054 次
发布时间:2019-04-28

本文共 55962 字,大约阅读时间需要 186 分钟。

Nodejs项目实战开发-博客系统(已完结)

个人博客系统

欢迎访问我的博客~

前言:

开发技术

技术 版本
Node ^14.3.0
ejs ^3.1.3
express ^4.17.1
cookie-session ^1.4.0
mysql ^2.18.1

开发工具

技术 版本
VSCode ^1.47.3.0
MySql ^8.0.12
PhpStudy ^8.1.0.7

第一阶段 博客前台

第二阶段 博客后台

博客前台 项目环境搭建

首先初始化项目

在终端输入命令

npm init

会生成一个 package.json 文件

安装一系列第三方npm模块

npm i expressnpm i ejsnpm i mysql

会生成一个 package-lock.json 文件

创建一系列文件

新建index.js文件

/** * 入口函数 */const express = require("express");// 创建主应用const app = express();// 模板引擎的设置app.set("view engine", "html");app.set("views", `${
__dirname}/views`);app.engine("html", require("ejs").renderFile); // 用ejs模板渲染html// 静态资源配置app.use(express.static("static"));// 监听服务器app.listen(3000);

修改 package.json 文件

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"},

自定义启动命令

把上面的代码改成以下代码

"scripts": {
"start": "node index.js"},

新建static目录

静态资源目录

里面存放 css js img 等等的静态资源

新建views目录

模板目录

里面存放模板文件

新建router目录

子应用目录

将我们的程序进行模块化管理,每一个模块都将视为一个子应用

新建middleware目录

中间件目录

与路由器配套的一些操作方法,在路由加载页面之前获取所需要的一些数据

新建model目录

数据模型目录

操作数据库的一些文件

这些文件之间的一些关系

从数据库中进行增删改查,把得到的一些数据返回给中间件,由中间件返回给路由,由路由进行页面的渲染和加载,最后返回给客户端

博客前台 模板文件导入

在views目录和static目录下导入文件

导入完成之后可以把重复的代码提取出来,进行简化处理

<%- include('header.html') -%>html代码不重复的部分<%- include('footer.html') -%>

在router目录下新建index.js文件

/** * 首页子应用(首页路由) */const express = require("express");// 首页子应用const indexApp = express();indexApp.get("/", (req, res) => {
res.render("index");});// 把这个子应用导出去module.exports = indexApp;

然后在myblog下的 index.js 中调用首页子应用

// 调用首页子应用app.use(/\/(index)?/, require("./router/index"));

启动服务

node index.js

浏览器输入127.0.0.1:3000 出现以下页面说明搭建成功

a6aqIg.png

博客前台 封装数据库操作

在model目录下新建 model.js 文件

const mysql = require("mysql");/** * 数据模型的基类 * 封装了数据库操作 */module.exports = class Model {
// 连接对象 static conn = null; /** * 数据库连接方法 */ static connection() {
Model.conn = mysql.createConnection({
host: "127.0.0.1", user: "root", password: "lijiazhao123", database: "blog", }); Model.conn.connect((err) => {
if (err) {
console.log(`数据库连接失败:${
err.message}`); } }); } /** * 关闭数据库连接 */ static end() {
if (null != Model.conn) {
Model.conn.end(); } } /** * 通用查询方法 * @param {string} sql 要执行的SQL语句 * @param {Array} params 给SQL语句的占位符进行赋值的参数数组 */ static query(sql, params = []) {
return new Promise((resolve, reject) => {
this.connection(); Model.conn.query(sql, params, (err, results) => {
if (err) {
reject(err); } else {
resolve(results); } }); this.end(); }); }};

在model目录下新建 article.js 文件

/** * 文章数据模型 */module.exports = class Article extends require("./model") {
/** * * @param {integer} num 条目数 */ static getHot(num) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,content,`time` FROM article WHERE hot = 1 LIMIT ?"; this.query(sql, num) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取热门文章失败:${
err.message}`); reject(err); }); }); }};

在middleware目录下新建 article.js 文件

const Article = require("../model/article");/** * 文章中间件 */module.exports = {
/** * 获取热门文章 */ getHot: (req, res, next) => {
Article.getHot(3) .then((results) => {
req.hots = results; next(); }) .catch((err) => {
next(err); }); },};

再由中间件返回给路由

将router目录下的 index.js 进行修改

/** * 首页子应用(首页路由) */const express = require("express");const article = require("../middleware/article");// 首页子应用const indexApp = express();indexApp.get("/", [article.getHot], (req, res) => {
res.render("index", {
hots: req.hots });});// 把这个子应用导出去module.exports = indexApp;

启动服务显示以下页面

acXJzD.png

发现这个时间的格式有点别扭,修改为本地时间

<%= hot.time.toLocaleString() %>

发现这个内容有点别扭

<%= hot.content.replace(/<[^>]+>/g,"").substring(0,100) %>

修改后如下图:

agNIp9.png

博客前台 首页最新博文

数据库查询

在model目录下的 article.js 文件中新增以下方法

/**  * 获取文章列表  */  static getList() {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,content,`time` FROM article ORDER BY time DESC"; this.query(sql) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取文章列表失败:${
err.message}`); reject(err); }); }); }

把查询到的数据交给中间件

在middleware目录下的 article.js 文件中新增以下方法

/**  * 获取最新文章  */  getList: (req, res, next) => {
Article.getList() .then((results) => {
req.articles = results; next(); }) .catch((err) => {
next(err); }); },

在由中间件交给路由渲染到页面中

在router目录下的 index.js 文件中修改以下代码

indexApp.get("/", [article.getHot, article.getList], (req, res) => {
let {
hots, articles } = req; res.render("index", {
hots, articles });});

开启服务,出现以下页面

a2sUVs.png

博客前台 导航条

数据库查询

在model目录下新建 category.js 文件

/** * 文章类目数据模型 */module.exports = class Category extends require("./model") {
/** * 获取文章类目列表 */ static getList() {
return new Promise((resolve, reject) => {
let sql = "SELECT id,`name` FROM category ORDER BY `index` DESC"; this.query(sql) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取文章类目列表失败:${
err.message}`); reject(err); }); }); }};

把查询到的数据交给中间件

在middlewar目录下新建 category.js 文件

const Category = require("../model/category");/** * 文章类目中间件 */module.exports = {
/** * 获取文章类目列表 */ getList: (req, res, next) => {
Category.getList() .then((results) => {
req.categories = results; next(); }) .catch((err) => {
next(err); }); },};

在由中间件交给路由渲染到页面中

修改router目录下的 index.js 文件

/** * 首页子应用(首页路由) */const express = require("express");const article = require("../middleware/article");const category = require("../middleware/category");// 首页子应用const indexApp = express();indexApp.get(  "/",  [article.getHot, article.getList, category.getList],  (req, res) => {
let {
hots, articles, categories } = req; res.render("index", {
hots, articles, categories }); });// 把这个子应用导出去module.exports = indexApp;

修改views下的 header.html 文件

用ejs模板来显示路由渲染过来的数据

<% categories.forEach(category => { %>    <% }) %>

启动服务。显示页面如下

a2XRwd.png

博客前台 文章列表

在model目录下的 article.js 文件中新增方法

/**   * 获取指定类目下的文章列表   * @param {integer} id 类目编号   */  static getListByCategoryId(id) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,content,`time` FROM article WHERE category_id = ? ORDER BY time DESC"; this.query(sql, id) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取指定类目下的文章列表失败:${
err.message}`); reject(err); }); }); }

在middleware目录下的 article.js 文件新增属性

/**   * 获取指定类目下的文章   */  getListByCategoryId: (req, res, next) => {
/** * http://127.0.0.1:3000/article/list/1 * 获取路由下的id - 此时id为1 */ let id = req.params.id; Article.getListByCategoryId(id) .then((results) => {
req.articles = results; next(); }) .catch((err) => {
next(err); }); },

在router目录下新建 article.js 文件

/** * 文章子应用 */const express = require("express");const article = require("../middleware/article");const category = require("../middleware/category");// 文章子应用const articleApp = express();articleApp.get(  "/list/:id",  [article.getListByCategoryId, category.getList],  (req, res) => {
let {
articles, categories } = req; res.render("list", {
articles, categories }); });module.exports = articleApp;

修改views目录下的 list.html 文件

<% articles.forEach(article => { %>    
...
<%= article.title %>

<%= article.time.toLocaleString() %>

<%= article.content.replace(/<[^>]+>/g,"").substring(0,100) %>...

<% }) %>

最后在myblog目录下的 index.js 入口函数中新增

app.use("/article", require("./router/article"));

启动服务,浏览器访问 http://127.0.0.1:3000/article/list/1 ,出现以下页面

aRnmCV.png

虽然现在输入url网址能够访问到指定类目列表,但是点击类目列表却不能访问

这时候应该去 header.html 模板文件中修改 href 路由

<% categories.forEach(category => { %>    <% }) %>

然后就是修改当前栏目后面显示的数据

通过id查找数据库中的name,然后进行数据显示

在model目录下的 category.js 文件中新增方法

/**   * 获取指定编号的类目详情   * @param {integer} id 类目编号   */  static getCategoryById(id) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,`name`,`index` FROM category WHERE id = ?"; this.query(sql, id) .then((results) => {
resolve(results[0]); }) .catch((err) => {
console.log(`获取指定编号的类目详情失败:${
err.message}`); reject(err); }); }); }

在middleware目录下新增属性

/**   * 获取指定的类目详情   */  getCategoryById: (req, res, next) => {
let id = req.params.id; Category.getCategoryById(id) .then((results) => {
req.category = results; next(); }) .catch((err) => {
next(err); }); },

修改router目录下的 article.js 文件

articleApp.get(  "/list/:id",  [article.getListByCategoryId, category.getList, category.getCategoryById],  (req, res) => {
let {
articles, categories, category } = req; res.render("list", {
articles, categories, category }); });

修改views目录下的 list.html 模板文件

当前栏目:<%= category.name %>

启动服务,出现以下页面

aR3ffA.png

博客前台 文章搜索

在model目录下的 article.js 文件下新增方法

/**   * 获取指定关键词的文章列表   * @param {integer} keyword 搜索内容   */  static getListByKeyword(keyword) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,content,`time` FROM article WHERE title LIKE ? ORDER BY time DESC"; this.query(sql, `%${
keyword}%`) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取指定关键词的文章列表失败:${
err.message}`); reject(err); }); }); }

在middleware目录下的 article.js 文件中新增属性

/**   * 获取指定类目下的文章   */  getListByKeyword: (req, res, next) => {
/** * 要获取表单的keyword */ let keyword = req.query.keyword; Article.getListByKeyword(keyword) .then((results) => {
req.articles = results; next(); }) .catch((err) => {
next(err); }); },

在router目录下新建文件 search.js

/** * 搜索子应用 */const express = require("express");const article = require("../middleware/article");const category = require("../middleware/category");// 文章子应用const searchApp = express();searchApp.get("/", [article.getListByKeyword, category.getList], (req, res) => {
let {
articles, categories } = req; res.render("search", {
articles, categories, keyword: req.query.keyword });});module.exports = searchApp;

修改views目录下的 search.html 模板文件

<%- include("header.html") -%>

搜索结果(搜索词:<%= keyword %> )

<% articles.forEach(article => { %>
...
<%= article.title %>

<%= article.time.toLocaleString() %>

<%= article.content.replace(/<[^>]+>/g,"").substring(0,100) %>...

<% }) %>
<%- include('footer.html') -%>

修改views目录下的 header.html 文件的form表单

启动服务,搜索css出现以下页面

aRwkNV.png

博客前台 文章详情

在router目录下新建 article.js 文件

// 文章详情页articleApp.get("/:id", (req, res) => {
let {
categories } = req; res.render("article", {
categories });});

启动服务,在浏览器输入url网址 http://127.0.0.1:3000/article/1 会出现以下页面

af4OZq.png

在model目录下的 article.js 文件中新增方法

/**   * 获取指定文章的详情   * @param {integer} id 文章编号   */  static getArticleById(id) {
return new Promise((resolve, reject) => {
let sql = "SELECT a.id,a.title,a.content,a.`time`,a.hits,a.category_id,c.`name` FROM article a,category c WHERE a.id = ? AND a.category_id = c.id"; this.query(sql, id) .then((results) => {
resolve(results[0]); }) .catch((err) => {
console.log(`获取指定文章的详情失败:${
err.message}`); reject(err); }); }); }

在middleware目录下的 article.js 文件中新增属性

/**   * 获取指定文章的详情   */  getArticleById: (req, res, next) => {
let id = req.params.id; Article.getArticleById(id) .then((results) => {
req.article = results; next(); }) .catch((err) => {
next(err); }); },

修改router目录下的 article.js 文件

// 文章详情页articleApp.get("/:id", [article.getArticleById], (req, res) => {
let {
categories, article } = req; res.render("article", {
categories, article });});

修改views目录下的 article.html 文件

<%- include("header.html") -%>

<%= article.title %>

发表时间:<%= article.time.toLocaleString() %> 点击:<%= article.hits %>

<%- article.content %>
<%- include('footer.html') -%>

注意:<%- article.content %> 用 ‘-’ 可以解析HTML代码

然后更改各文件路由

按需求来更改路由即可

参考路由

href="/article/<%= article.id %>"

启动服务,基本完成文章跳转功能

博客前台 文章标签显示

在model目录下新建 tab.js 文件

/** * 标签数据模型 */module.exports = class Tab extends require("./model") {
/** * 获取指定文章的标签列表 * @param {integer} id 文章编号 */ static getListByArticleId(id) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,`name` FROM tabs WHERE article_id = ?"; this.query(sql, id) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取指定文章的标签列表失败:${
err.message}`); reject(err); }); }); }};

在middleware目录下的 article.js 文件下新增属性

/**   * 获取指定文章的标签列表   */  getListByArticleId: (req, res, next) => {
let id = req.params.id; Tab.getListByArticleId(id) .then((results) => {
req.tabs = results; next(); }) .catch((err) => {
next(err); }); },

修改router目录下的 article.js 文件

// 文章详情页articleApp.get(  "/:id",  [article.getArticleById, article.getListByArticleId],  (req, res) => {
let {
categories, article, tabs } = req; res.render("article", {
categories, article, tabs }); });

修改views目录下的 article.js 文件

开启服务,出现以下页面

ahm6LF.png

博客前台 上一篇下一篇

在model目录下的 article.js 文件中新建两个方法

/**   * 获取上一篇文章   * @param {integer} id 当前文章编号   */  static getPrevArticle(id) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title FROM article WHERE id < ? ORDER BY id DESC LIMIT 1"; this.query(sql, id) .then((results) => {
resolve(results[0]); }) .catch((err) => {
console.log(`获取上一篇文章失败:${
err.message}`); reject(err); }); }); } /** * 获取下一篇文章 * @param {integer} id 当前文章编号 */ static getNextArticle(id) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title FROM article WHERE id > ? ORDER BY id ASC LIMIT 1"; this.query(sql, id) .then((results) => {
resolve(results[0]); }) .catch((err) => {
console.log(`获取下一篇文章失败:${
err.message}`); reject(err); }); }); }

在middleware目录下的 article.js 文件中新增两个属性

/**   * 获取上一篇文章   */  getPrevArticle: (req, res, next) => {
let id = req.params.id; Article.getPrevArticle(id) .then((results) => {
req.prev = results; next(); }) .catch((err) => {
next(err); }); }, /** * 获取下一篇文章 */ getNextArticle: (req, res, next) => {
let id = req.params.id; Article.getNextArticle(id) .then((results) => {
req.next = results; next(); }) .catch((err) => {
next(err); }); },

修改router目录下的 article.js 文件

// 文章详情页articleApp.get(  "/:id",  [    article.getArticleById,    article.getListByArticleId,    article.getPrevArticle,    article.getNextArticle,  ],  (req, res) => {
let {
categories, article, tabs, prev, next } = req; res.render("article", {
categories, article, tabs, prev, next }); });

修改views目录下的 article.html 文件

开启服务,页面完成以下效果

ah8kRJ.gif

博客前台 用户登录

首先在router目录下新建 login.js 文件

/** * 登录子应用(首页路由) */const express = require("express");// 登录子应用const loginApp = express();// 加载登录页面loginApp.get("/", (req, res) => {
res.render("login");});// 把这个子应用导出去module.exports = loginApp;

在myblog目录下的 index.js 文件中调用登录子应用

// 调用登录子应用app.use("/login", require("./router/login"));

在model目录下新建 user.js 文件

/** * 用户数据模型 */module.exports = class User extends require("./model") {
/** * 用户登录 * @param {string} username 登录账号 * @param {string} password 登录密码 */ static login(username, password) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,username FROM `user` WHERE username=? AND `password`=?"; this.query(sql, [username, password]) .then((results) => {
resolve(results[0]); }) .catch((err) => {
console.log(`登录失败:${
err.message}`); reject(err); }); }); }};

因为用户登录不用中间件,所以直接在router目录下的 login.js 文件中实现登录操作

// 实现登录操作loginApp.post("/", (req, res, next) => {
let {
username, password } = req.body; User.login(username, password) .then((results) => {
if (results) {
res.redirect("/"); } else {
res.render("login", {
msg: "登录失败!用户名或密码错误" }); } }) .catch((err) => {
next(err); });});

把登录失败的数据映射到页面上

修改在views目录下的 login.html 文件

<%= msg %>

在myblog下的 index.js 文件中进行post请求处理

// POST请求处理app.use(express.urlencoded({
extended: true }));

开启服务,出现以下效果

[ahcP2R.gif

博客前台 Session与退出

安装cookie-session模块

npm i cookie-session

在myblog下的 index.js 文件中引入cookie-session

const session = require("cookie-session");

配置session

// SESSION配置app.use(  session({
keys: ["secret"], maxAge: 1000 * 60 * 30, // cookie的生命周期 }));

修改router目录下的 login.js 文件的登录操作功能

用户登录后会把从数据库查询到的结果results保存到session的user中

// 实现登录操作loginApp.post("/", (req, res, next) => {
let {
username, password } = req.body; User.login(username, password) .then((results) => {
if (results) {
// session存储(key=value) req.session.user = results; res.redirect("/"); } else {
res.render("login", {
msg: "登录失败!用户名或密码错误" }); } }) .catch((err) => {
next(err); });});

在middleware目录下新建 auth.js 文件

/** * 权限中间件 */module.exports = {
/** * 从session中读取用户 */ getUser: (req, res, next) => {
// 从session中读取数据 req.user = req.session.user; next(); },};

article.jsindex.jssearch.js 文件中都加入user,目的是为了使未登录的用户不能访问这些页面。

const auth = require("../middleware/auth");articleApp.use(category.getList, auth.getUser);// 文章列表页articleApp.get(  "/list/:id",  [article.getListByCategoryId, category.getCategoryById],  (req, res) => {
let {
articles, categories, category, user } = req; res.render("list", {
articles, categories, category, user }); });// 文章详情页articleApp.get( "/:id", [ article.getArticleById, article.getListByArticleId, article.getPrevArticle, article.getNextArticle, ], (req, res) => {
let {
categories, article, tabs, prev, next, user } = req; res.render("article", {
categories, article, tabs, prev, next, user }); });
const auth = require("../middleware/auth");indexApp.use(auth.getUser);// 加载首页页面indexApp.get(  "/",  [article.getHot, article.getList, category.getList],  (req, res) => {
let {
hots, articles, categories, user } = req; res.render("index", {
hots, articles, categories, user }); });
const auth = require("../middleware/auth");searchApp.use(auth.getUser);searchApp.get("/", [article.getListByKeyword, category.getList], (req, res) => {
let {
articles, categories, user } = req; res.render("search", {
articles, categories, keyword: req.query.keyword, user, });});

修改views目录下的 header.html 文件,加入判断

如果user存在,说明已登录,则显示用户数据

如果user不存在,说明未登录,则显示登录

启动服务,用户未登录显示以下效果

aho8pT.png

用户已登录显示以下效果

ahowA1.png

在myblog目录下的 index.js 文件中实现退出登录

// 退出登录app.get("/user/logout", (req, res) => {
req.session.user = null; res.render("login", {
msg: "退出成功" });});

启动服务,实现以下效果

ah7SII.gif

博客后台 功能介绍与模板整合

在views目录下新建admin后台管理目录

把需要的资源复制进来

同样,在static目录下新建admin目录

把需要的资源复制进来

pv.json 文件复制到myblog目录下

博客后台 模板管理

为了方便,把代码重复的部分提取到 header.html 模板中

然后用ejs模板引入即可

在router目录下新建admin目录,在router/admin目录下新建 index.js 文件

/** * 后台首页 */const express = require("express");const indexApp = express();indexApp.get("/", (req, res) => {
res.render("admin/index");});module.exports = indexApp;

在myblog目录下的 index.js 文件中调用后台首页

// 调用后台首页app.use(/\/admin\/(index)?/, require("./router/admin/index"));

在middleware目录下的 auth.js 文件中新增属性

/**   * 是否允许用户进入后台管理页   */  allowToAdmin: (req, res, next) => {
let user = req.session.user; if (user) {
req.user = user; next(); } else {
res.redirect("/login"); } },

myblog/index.js 中加上进入后台的权限验证

// 进入后台的权限验证app.use("/admin/?*", require("./middleware/auth").allowToAdmin);

这样写就可以使所有的后台页面都需要登录之后才能访问

修改 views/admin/header.html 文件

views/header.html 中新增个人中心

博客后台 获取最后登录时间

model/user.js 文件中新增方法

/**   * 最后一次登陆的时间   */  static lastLoginTime() {
return new Promise((resolve, reject) => {
let sql = "SELECT time FROM log WHERE handle = '登录' ORDER BY time DESC LIMIT 1"; this.query(sql) .then((results) => {
resolve(results[0]); }) .catch((err) => {
console.log(`登录失败:${
err.message}`); reject(err); }); }); }

在middleware目录下新建 user.js 文件

/** * 用户中间件 */const User = require("../model/user");module.exports = {
/** * 最后一次登录时间 */ lastLoginTime: (req, res, next) => {
User.lastLoginTime() .then((results) => {
req.lastLoginTime = results; next(); }) .catch((err) => {
next(err); }); },};

修改 router/admin/index.js 文件

/** * 后台首页 */const express = require("express");const user = require("../../middleware/user");const indexApp = express();indexApp.get("/", [user.lastLoginTime], (req, res) => {
let {
user, lastLoginTime } = req; res.render("admin/index", {
user, lastLoginTime });});module.exports = indexApp;

修改 views/admin/index.html 文件

上次登录时间:<%= lastLoginTime.time.toLocaleString() %>

博客后台 访问量、博文和类目的统计

在model目录下新建 pv.js 文件

/** * 访问量数据模型 */module.exports = class PV extends require("./model") {
/** * 获取总访问量 */ static getTotal() {
return new Promise((resolve, reject) => {
let sql = "SELECT SUM(hits) AS total FROM pv"; this.query(sql) .then((results) => {
resolve(results[0].total); }) .catch((err) => {
console.log(`获取总访问量失败:${
err.message}`); reject(err); }); }); }};

model/article.js 文件中新增方法

/**   * 总博文数   */  static getCount() {
return new Promise((resolve, reject) => {
let sql = "SELECT COUNT(1) AS count FROM article"; this.query(sql) .then((results) => {
resolve(results[0].count); }) .catch((err) => {
console.log(`获取总博文数失败:${
err.message}`); reject(err); }); }); }

model/category.js 文件中新增方法

/**   * 总类目数   */  static getCount() {
return new Promise((resolve, reject) => {
let sql = "SELECT COUNT(1) AS count FROM category"; this.query(sql) .then((results) => {
resolve(results[0].count); }) .catch((err) => {
console.log(`获取总类目数失败:${
err.message}`); reject(err); }); }); }

在middleware目录下新建 pv.js 文件

/** * 访问量中间件 */const Pv = require("../model/pv");module.exports = {
/** * 获取总访问量 */ getTotal: (req, res, next) => {
Pv.getTotal() .then((results) => {
req.pvTotal = results; next(); }) .catch((err) => {
next(err); }); },};

middleware/article.js 文件中新建属性

/**   * 获取总博文数   */  getCount: (req, res, next) => {
Article.getCount() .then((results) => {
req.articleCount = results; next(); }) .catch((err) => {
next(err); }); },

middleware/category.js 文件中新建属性

/**   * 获取总类目数   */  getCount: (req, res, next) => {
Category.getCount() .then((results) => {
req.categoryCount = results; next(); }) .catch((err) => {
next(err); }); },

修改 router/admin/index.js 文件

/** * 后台首页 */const express = require("express");const user = require("../../middleware/user");const pv = require("../../middleware/pv");const category = require("../../middleware/category");const article = require("../../middleware/article");const indexApp = express();indexApp.get(  "/",  [user.lastLoginTime, pv.getTotal, category.getCount, article.getCount],  (req, res) => {
let {
user, lastLoginTime, pvTotal, categoryCount, articleCount } = req; res.render("admin/index", {
user, lastLoginTime, pvTotal, categoryCount, articleCount, }); });module.exports = indexApp;

修改 views/admin/index.html 文件

总访问量
<%= pvTotal %>
总博文数
<%= articleCount %>
总类目数
<%= categoryCount %>

博客后台 获取访问量趋势图数据

model/pv.js 文件中新建获取全部记录的方法

/**   * 获取全部记录   */  static getAll() {
return new Promise((resolve, reject) => {
let sql = "SELECT time,hits FROM pv ORDER BY time ASC"; this.query(sql) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取全部记录失败:${
err.message}`); reject(err); }); }); }

middleware/pv.js 文件中新建获取全部记录的属性

/**   * 获取全部记录   */  getAll: (req, res, next) => {
Pv.getAll() .then((results) => {
req.pvs = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/index.js 文件中新建访问量接口

/** * 访问量接口 */indexApp.get("/pvs", [pv.getAll], (req, res) => {
let {
pvs } = req; let data = {
}; data.data = pvs; data.start = pvs[0].time; data.end = pvs[pvs.length - 1].time; res.json(data);});

static/admin/js/public.js 文件中的url改为 /admin/index/pvs

let url = "/admin/index/pvs";

博客后台 管理导航处理

views/admin 目录下新建 navs.html 文件,是重复代码通用模板

然后按需替换即可

替换语法:

<%- include("navs.html") -%>

router/admin 目录下新建文件

category.js - 后台类目管理

/** * 后台类目管理 */const express = require("express");const categoryApp = express();categoryApp.get("/", (req, res) => {
res.render("admin/category/index", {
user: req.user });});module.exports = categoryApp;

article.js - 后台文章管理

/** * 后台文章管理 */const express = require("express");const articleApp = express();articleApp.get("/", (req, res) => {
res.render("admin/article/index", {
user: req.user });});module.exports = articleApp;

log.js - 后台日志管理

/** * 后台日志管理 */const express = require("express");const logApp = express();logApp.get("/", (req, res) => {
res.render("admin/log/index", {
user: req.user });});module.exports = logApp;

account.js - 后台账户管理

/** * 后台账户管理 */const express = require("express");const accountApp = express();accountApp.get("/", (req, res) => {
res.render("admin/account/index", {
user: req.user });});module.exports = accountApp;

myblog/index.js 中调用路由

// 调用后台文章管理app.use("/admin/article", require("./router/admin/article"));// 调用后台类目管理app.use("/admin/category", require("./router/admin/category"));// 调用后台日志管理app.use("/admin/log", require("./router/admin/log"));// 调用后台账户管理app.use("/admin/account", require("./router/admin/account"));

修改 views/admin 目录下的 navs.html 文件中的路由

最后在 static/js/public.js 中写个判断

if ($(".list-group-item").length) {
let href = location.pathname; console.log(href); $(`.list-group-item a[href='${
href}']`).parent().addClass("active");}

开启服务,页面出现以下效果

博客后台 文章列表显示

model/article.js 文件中新建获取指定页文章列表的方法

/**   * 获取指定页文章列表   */  static getPage() {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,thumbnail,hot FROM article ORDER BY time DESC"; this.query(sql) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取指定页文章列表失败:${
err.message}`); reject(err); }); }); }

middleware/article.js 文件中新建获取指定页文章列表的属性

/**   * 获取指定页的文章列表   */  getPage: (req, res, next) => {
Article.getPage() .then((results) => {
req.pageList = results; next(); }) .catch((err) => {
next(err); }); },};

router/admin/article.js 文件对pageList进行封装处理,为后面的分页操作打基础

/** * 后台文章管理 */const express = require("express");const article = require("../../middleware/article");const articleApp = express();articleApp.get("/", [article.getPage], (req, res) => {
let {
user, pageList } = req; let page = {
}; page.list = pageList; res.render("admin/article/index", {
user, page });});module.exports = articleApp;

views/admin/article/index.html 中的文件进行修改,用取得的数据库的数据重新渲染页面

    <% page.list.forEach(article => { %>                    <%= article.id %>            <%= article.title %>                            <% if (article.thumbnail) { %>                                        <% } %>                                                                    
<%= article.hot?'checked':'' %> />
编辑 删除 <% }) %>

启动服务,呈现以下效果

博客后台 文章列表分页

router/admin/article.js 文件中对articleCount等的封装

/** * 后台文章管理 */const express = require("express");const article = require("../../middleware/article");const articleApp = express();articleApp.get("/", [article.getPage, article.getCount], (req, res) => {
let {
user, pageList, articleCount } = req; let size = 5; // 每页显示5条 let page = {
}; page.count = articleCount; // 总页数 page.total = Math.ceil(page.count / size); // 最大页数 page.list = pageList; page.p = req.query.p ? req.query.p : 1; // 页数 page.p = page.p > page.total ? page.total : page.p; page.p = page.p < 1 ? 1 : page.p; res.render("admin/article/index", {
user, page });});module.exports = articleApp;

修改 views/admin/index.html 文件

共 <%= page.count %> 条 / 共 <%= page.total %> 页 / 第 <%=page.p %> 页

开启服务,浏览器访问http://127.0.0.1:3000/admin/article?p=7,页面显示以下效果

model/article.js 文件中新建获取指定页文章列表的方法

/**   * 获取指定页文章列表   * @param {integer} start 起始索引   * @param {integer} size 查询条目数   */  static getPage(start, size) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,thumbnail,hot FROM article ORDER BY time DESC LIMIT ?,?"; this.query(sql, [start, size]) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取指定页文章列表失败:${
err.message}`); reject(err); }); }); }

middleware/article.js 文件中的getPage属性传上刚加上的参数

/**   * 获取指定页的文章列表   */  getPage: (req, res, next) => {
Article.getPage(res.start, res.size) .then((results) => {
req.pageList = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/article.js 文件进行修改 - 异步操作

/** * 后台文章管理 */const express = require("express");const article = require("../../middleware/article");const articleApp = express();articleApp.get(  "/",  [article.getCount],  (req, res, next) => {
let {
articleCount } = req; let size = 5; // 每页显示5条 req.page = {
}; req.page.count = articleCount; // 总页数 req.page.total = Math.ceil(req.page.count / size); // 最大页数 req.page.p = req.query.p ? req.query.p : 1; // 页数 req.page.p = req.page.p > req.page.total ? req.page.total : req.page.p; req.page.p = req.page.p < 1 ? 1 : req.page.p; res.start = (req.page.p - 1) * size; res.size = size; next(); }, [article.getPage], (req, res) => {
let {
user, pageList, page } = req; page.list = pageList; res.render("admin/article/index", {
user, page }); });module.exports = articleApp;

修改 views/admin/article/index.html 文件

启动服务,如下效果

博客后台 文章列表分页查询

router/admin/article.js 文件中新增类目列表数据

/** * 后台文章管理 */const express = require("express");const article = require("../../middleware/article");const category = require("../../middleware/category");const articleApp = express();articleApp.get(  "/",  [article.getCount],  (req, res, next) => {
let {
articleCount } = req; let size = 5; // 每页显示5条 req.page = {
}; req.page.count = articleCount; // 总页数 req.page.total = Math.ceil(req.page.count / size); // 最大页数 req.page.p = req.query.p ? req.query.p : 1; // 页数 req.page.p = req.page.p > req.page.total ? req.page.total : req.page.p; req.page.p = req.page.p < 1 ? 1 : req.page.p; res.start = (req.page.p - 1) * size; res.size = size; next(); }, [article.getPage, category.getList], (req, res) => {
let {
user, pageList, page, categories } = req; page.list = pageList; res.render("admin/article/index", {
user, page, categories }); });module.exports = articleApp;

修改 views/admin/article/index.html 文件

修改 model/article.js 文件,附带两个参数category_id, hot

/**   * 总博文数   */  static getCount(category_id, hot) {
return new Promise((resolve, reject) => {
let sql = "SELECT COUNT(1) AS count FROM article WHERE 1=1"; sql += category_id != -1 && category_id ? ` AND category_id=${
category_id}` : ""; sql += hot != -1 && hot ? ` AND hot=${
hot}` : ""; this.query(sql) .then((results) => {
resolve(results[0].count); }) .catch((err) => {
console.log(`获取总博文数失败:${
err.message}`); reject(err); }); }); } /** * 获取指定页文章列表 * @param {integer} start 起始索引 * @param {integer} size 查询条目数 */ static getPage(start, size, category_id, hot) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,thumbnail,hot FROM article WHERE 1=1"; sql += category_id != -1 && category_id ? ` AND category_id=${
category_id}` : ""; sql += hot != -1 && hot ? ` AND hot=${
hot}` : ""; sql += " ORDER BY time DESC LIMIT ?,?"; this.query(sql, [start, size]) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取指定页文章列表失败:${
err.message}`); reject(err); }); }); }

再修改 middleware/article.js 文件

/**   * 获取总博文数   */  getCount: (req, res, next) => {
Article.getCount(req.query.category_id, req.query.hot) .then((results) => {
req.articleCount = results; next(); }) .catch((err) => {
next(err); }); }, /** * 获取指定页的文章列表 */ getPage: (req, res, next) => {
Article.getPage(res.start, res.size, req.query.category_id, req.query.hot) .then((results) => {
req.pageList = results; next(); }) .catch((err) => {
next(err); }); },

再到路由中进行修改 router/admin/article.js

/** * 后台文章管理 */const express = require("express");const article = require("../../middleware/article");const category = require("../../middleware/category");const articleApp = express();articleApp.get(  "/",  [article.getCount],  (req, res, next) => {
let {
articleCount } = req; let size = 3; // 每页显示5条 req.page = {
}; req.page.count = articleCount; // 总页数 req.page.total = Math.ceil(req.page.count / size); // 最大页数 req.page.p = req.query.p ? req.query.p : 1; // 页数 req.page.p = req.page.p > req.page.total ? req.page.total : req.page.p; req.page.p = req.page.p < 1 ? 1 : req.page.p; res.start = (req.page.p - 1) * size; res.size = size; next(); }, [article.getPage, category.getList], (req, res) => {
let {
user, pageList, page, categories } = req; let {
category_id, hot } = req.query; page.list = pageList; res.render("admin/article/index", {
user, page, categories, category_id, hot, }); });module.exports = articleApp;

修改 views/admin/article/index.html 文件

    <% if (page.p!=1) { %>
  • 上一页
  • <% } %> <% for( let index = 1; index <= page.total; index++ ) { %>
  • <%= index %>
  • <% } %> <% if (page.p!=page.total) { %>
  • 下一页
  • <% } %>

博客后台 文章列表-热门推荐

首先在 model/article.js 文件中设置热门方法

/**   * 设置热门   * @param {integer} id 文章编号   * @param {integer} hot 热门状态   */  static setHot(id, hot) {
return new Promise((resolve, reject) => {
let sql = "UPDATE article SET hot = ? WHERE id = ?"; this.query(sql, [hot, id]) .then((results) => {
resolve(results.affectedRows); }) .catch((err) => {
console.log(`设置热门失败:${
err.message}`); reject(err); }); }); }

然后再 middleware/article.js 文件中设置热门推荐属性

/**   * 设置热门推荐   */  setHot: (req, res, next) => {
let {
id, hot } = req.query; Article.setHot(id, hot) .then((results) => {
req.affectedRows = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/article.js 文件中设置热门推荐路由

articleApp.get("/sethot", article.setHot, (req, res) => {
// 如果受影响的行数大于0,说明数据库更新成功,否则失败 if (req.affectedRows > 0) {
res.json({
code: 1, msg: "设置成功" }); } else {
res.json({
code: 0, msg: "设置失败" }); }});

views/admin/article/index.html 文件中进行修改

首先是页面部分

<%= article.hot?'checked':'' %> οnchange="sethot(this.value,this.checked)" />

其次是JavaScript脚本部分

function sethot(id,hot){
$.get("/admin/article/sethot",{
id,hot:hot?1:0},function(res){
if(res.code==1){
showToasts("成功","设置热门成功") }else{
showToasts("失败","设置热门失败") } })}

启动服务,即可完成页面点击热门推荐按钮对数据库的hot进行修改

博客后台 添加博文-页面显示

router/admin/article.js 文件中新增显示添加博文的方法

// 显示添加博文页articleApp.get("/add", [category.getList], (req, res) => {
let {
user, categories } = req; res.render("admin/article/add", {
user, categories });});

博客后台 图片上传

安装multer

npm i multer

myblog/index.js 文件中引入multer

const multer = require("multer");

上传配置

// 上传配置const upload = multer({
dest: "./static/upload", // 上传文件的存储目录 limits: {
fileSize: 1024 * 1024 * 2, // 单个文件大小限制在2M },});

上传操作

const fs = require("fs");const path = require("path");// 上传操作app.post("/admin/*", upload.single("upload"), (req, res, next) => {
// 上传成功后的文件对象 let {
file } = req; if (file) {
// file.originalname ==> 文件的原名称 let extname = path.extname(file.originalname); // file.path ==> 上传后的文件路径 fs.renameSync(file.path, file.path + extname); // file.filename ==> 上传后的文件名 req.uploadUrl = "/upload/" + file.filename + extname; } next();});

router/admin/article.js 文件中新增上传图片的路由

// ckeditor 上传articleApp.post("/ckeditor", (req, res) => {
if (req.uploadUrl) {
res.json({
uploaded: true, url: req.uploadUrl, }); } else {
res.json({
uploaded: false, err: {
message: "上传失败" }, }); }});

完成如下效果

博客后台添加博文-数据库写入

model.article.js 中新增添加文章方法

/**   * 添加文章   * @param {Object} article 文章对象   */  static add(article) {
return new Promise((resolve, reject) => {
let sql = "INSERT INTO article SET ?"; this.query(sql, article) .then((results) => {
resolve(results.insertId); }) .catch((err) => {
console.log(`添加文章失败:${
err.message}`); reject(err); }); }); }

middleware/article.js 文件中新增添加文章的属性

/**   * 添加文章   */  add: (req, res, next) => {
let {
title, content, hot, category_id } = req.body; let article = {
title, content, hot: hot ? 1 : 0, category_id: category_id, thumbnail: req.uploadUrl ? req.uploadUrl : null, }; Article.add(article) .then((results) => {
req.insertId = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/article.js 文件中新增添加文章的路由

// 添加文章articleApp.post("/add", [article.add, category.getList], (req, res) => {
let {
user, categories } = req; if (req.insertId) {
res.render("admin/article/add", {
user, categories, code: 1 }); } else {
res.render("admin/article/add", {
user, categories, code: 2 }); }});

启动服务,上传文章到数据库

博客后台 删除博文

model/article.js 文件中新增删除文章的方法

/**   * 删除文章   * @param {integer} id 文章编号   */  static del(id) {
return new Promise((resolve, reject) => {
let sql = "DELETE FROM article WHERE id = ?"; this.query(sql, id) .then((results) => {
resolve(results.affectedRows); }) .catch((err) => {
console.log(`删除文章失败:${
err.message}`); reject(err); }); }); }

middleware/article.js 文件中新增删除文章的属性

/**   * 删除文章   */  del: (req, res, next) => {
let {
id } = req.query; Article.del(id) .then((results) => {
req.affectedRows = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/article.js 文件中新增删除文章的路由

// 删除文章articleApp.get("/del", article.del, (req, res) => {
if (req.affectedRows > 0) {
res.json({
code: 1, msg: "删除成功" }); } else {
res.json({
code: 2, msg: "删除失败" }); }});

修改 views/admin/article/index.html 文件

function del(id){
$.getJSON("/admin/article/del",{
id},function(res){
if(res.code==1){
showToasts("成功","删除成功") // 重新加载页面 setTimeout(function(){
location.reload() },2000) }else{
showToasts("失败","删除失败") } })}

启动服务,删除文章删除成功

博客后台 编辑博文-显示数据

model/article.js 文件中修改获取指定文章详情的方法

/**   * 获取指定文章的详情   * @param {integer} id 当前文章编号   */  static getArticleById(id) {
return new Promise((resolve, reject) => {
let sql = "SELECT a.id,a.title,a.content,a.`time`,a.hits,a.category_id,c.`name`,a.`thumbnail`,a.`hot` FROM article a,category c WHERE a.id = ? AND a.category_id = c.id"; this.query(sql, id) .then((results) => {
resolve(results[0]); }) .catch((err) => {
console.log(`获取指定文章的详情失败:${
err.message}`); reject(err); }); }); }

router/admin/article.js 文件中新增路由

// 文章编辑articleApp.get(  "/edit/:id",  [category.getList, article.getArticleById],  (req, res) => {
let {
user, categories, article } = req; res.render("admin/article/edit", {
user, categories, article }); });

修改 views/admin/article/edit.html 文件

      
首页
<%- include("../header.html") -%>
<%- include("../navs.html") -%>

文章管理

编辑文章
* 必填项
* 必填项
* 必填项
<%= article.hot == 1 ? "checked" : "" %> />

启动服务,实现对应文章页面显示

博客后台 编辑博文-数据库修改

model/article.js 文件中新增编辑文章的方法

/**   * 编辑文章   * @param {Object} article 文章对象   */  static edit(article) {
return new Promise((resolve, reject) => {
let sql = "UPDATE article SET title = ?, content = ?, hot = ?, category_id = ?, thumbnail = ? WHERE id = ?"; this.query(sql, [ article.title, article.content, article.hot, article.category_id, article.thumbnail, article.id, ]) .then((results) => {
resolve(results.affectedRows); }) .catch((err) => {
console.log(`编辑文章失败:${
err.message}`); reject(err); }); }); }

middleware/article.js 文件中新增编辑文章的属性

/**   * 编辑文章   */  edit: (req, res, next) => {
let {
title, content, hot, category_id, thumbnail, id } = req.body; let article = {
title, content, hot: hot ? 1 : 0, category_id, thumbnail: req.uploadUrl ? req.uploadUrl : thumbnail, id, }; Article.edit(article) .then((results) => {
req.affectedRows = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/article.js 文件中新增路由

articleApp.post("/edit", [article.edit], (req, res) => {
if (req.affectedRows > 0) {
res.render("admin/alert", {
code: true, title: "成功提示", message: "文章编辑成功", url: "/admin/article/", }); } else {
res.render("admin/alert", {
code: false, title: "失败提示", message: "文章编辑失败", url: "/admin/article/" + req.body.id, }); }

博客后台 前台文章列表与文章内容调整

model/article.js 文件中修改两个方法,数据库查询的时候都加上thumbnail字段

/**   * 获取热门推荐   * @param {integer} num 条目数   */  static getHot(num) {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,content,`time`,thumbnail FROM article WHERE hot = 1 LIMIT ?"; this.query(sql, num) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取热门文章失败:${
err.message}`); reject(err); }); }); } /** * 获取文章列表 */ static getList() {
return new Promise((resolve, reject) => {
let sql = "SELECT id,title,content,`time`,thumbnail FROM article ORDER BY time DESC"; this.query(sql) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取文章列表失败:${
err.message}`); reject(err); }); }); }

修改 views/admin/index.html 文件

...

使前台页面效果如下

博客管理 类目管理-显示与添加

model/category.js 文件中添加新增类目方法

/**   * 新增类目   */  static add(name, index) {
return new Promise((resolve, reject) => {
let sql = "INSERT INTO category (`name`,`index`) VALUES (?,?)"; this.query(sql, [name, index]) .then((results) => {
resolve(results.insertId); }) .catch((err) => {
console.log(`新增类目失败:${
err.message}`); reject(err); }); }); }

middleware/category.js 文件中添加新增类目属性

/**   * 添加类目   */  add: (req, res, next) => {
let {
name, index } = req.body; Category.add(name, index) .then((results) => {
req.insertId = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/category.js 文件中新增路由

categoryApp.post("/add", [category.add], (req, res) => {
if (req.insertId) {
res.json({
code: 1, msg: "添加成功" }); } else {
res.json({
code: 1, msg: "添加失败" }); }});

views/admin/category/index.html 文件中编写JS脚本

function save(t) {
let name = $(t).parents("tr").find("[name='name']").val(); let index = $(t).parents("tr").find("[name='index']").val(); $.post("/admin/category/add", {
name, index }, function (res) {
if (res.code == 1) {
showToasts("成功", "添加类目成功"); setTimeout(function () {
location.reload(); }, 2000); } else {
showToasts("失败", "添加类目失败"); } });}

效果如下

博客后台 类目管理-删除与修改

model/category.js 文件中新增删除类目的方法

/**   * 删除类目   * @param {integer} id 类目编号   */  static del(id) {
return new Promise((resolve, reject) => {
let sql = "DELETE FROM category WHERE id = ?"; this.query(sql, id) .then((results) => {
resolve(results.affectedRows); }) .catch((err) => {
console.log(`删除类目失败:${
err.message}`); reject(err); }); }); }

middleware/category.js 文件中新增删除类目的属性

/**   * 删除类目   */  del: (req, res, next) => {
let {
id } = req.query; Category.del(id) .then((results) => {
req.affectedRows = results; next(); }) .catch((err) => {
next(err); }); },

router/admin/category.js 文件中新增删除类目的路由

categoryApp.get("/del", category.del, (req, res) => {
if (req.affectedRows > 0) {
res.json({
code: 1, msg: "删除成功" }); } else {
res.json({
code: 1, msg: "删除失败" }); }});

views/admin/category/index.html 文件中编写JS脚本

function del(id) {
if (confirm("确认删除?")) {
$.getJSON("/admin/category/del", {
id }, function (res) {
if (res.code == 1) {
showToasts("成功", "删除类目成功"); setTimeout(function () {
location.reload(); }, 2000); } else {
showToasts("失败", "删除类目失败"); } }); }}

开启服务,实现如下效果

博客后台 日志列表显示

model 目录下新建 log.js 文件

/** * 日志数据模型 */module.exports = class Tab extends require("./model") {
/** * 获取日志列表 */ static getPage(start, size) {
return new Promise((resolve, reject) => {
let sql = "SELECT handle,`time`,ip FROM `log` ORDER BY `time` DESC LIMIT ?,?"; this.query(sql, [start, size]) .then((results) => {
resolve(results); }) .catch((err) => {
console.log(`获取日志列表失败:${
err.message}`); reject(err); }); }); } /** * 获取日志总条目数 */ static getCount() {
return new Promise((resolve, reject) => {
let sql = "SELECT COUNT(1) as count FROM `log`"; this.query(sql) .then((results) => {
resolve(results[0].count); }) .catch((err) => {
console.log(`获取日志总条目数失败:${
err.message}`); reject(err); }); }); }};

middleware 目录下新建 log.js 文件

/** * 访问量中间件 */const Log = require("../model/log");module.exports = {
/** * 获取日志列表 */ getPage: (req, res, next) => {
let {
p, size } = req.page; Log.getPage((p - 1) * size, size) .then((results) => {
req.page.list = results; next(); }) .catch((err) => {
next(err); }); }, /** * 获取总条目数 */ getCount: (req, res, next) => {
Log.getCount() .then((results) => {
req.count = results; next(); }) .catch((err) => {
next(err); }); },};

router/admin 目录下新建 log.js 文件

/** * 后台日志管理 */const express = require("express");const log = require("../../middleware/log");const logApp = express();logApp.get(  "/",  log.getCount,  (req, res, next) => {
let page = {
p: req.query.p ? req.query.p : 1, count: req.count, size: 3, }; page.total = Math.ceil(page.count / page.size); page.p = page.p > page.total ? page.total : page.p; page.p = page.p < 1 ? 1 : page.p; req.page = page; next(); }, log.getPage, (req, res) => {
let {
user, page } = req; res.render("admin/log/index", {
user, page }); });module.exports = logApp;

修改 views/admin/log/index.html 文件

      
首页
<%- include("../header.html") -%>
<%- include("../navs.html") -%>

查看日志

日志列表
<% page.list.forEach(log => { %>
<% }) %>
时间 操作 IP
<%= log.time.toLocaleString() %> <%= log.handle %> <%= log.ip %>

启动服务,实现以下效果

博客后台 日志列表显示

示例:做一个登录日志

router/admin/login.js 中修改实现登录操作的方法

const log = require("../middleware/log");// 实现登录操作loginApp.post("/", (req, res, next) => {
let {
username, password } = req.body; User.login(username, password) .then((results) => {
if (results) {
req.log = {
time: new Date(), handle: "登录", ip: req.ip.split(":")[3], }; log.add(req, res, next); // session存储(key=value) req.session.user = results; res.redirect("/"); } else {
res.render("login", {
msg: "登录失败!用户名或密码错误" }); } }) .catch((err) => {
next(err); });});

主要代码:

req.log = {
time: new Date(), handle: "登录", ip: req.ip.split(":")[3],};log.add(req, res, next);

自由发挥即可

博客后台 会话延期

cookie-session会话延期实现

myblog/index.js 文件中新建SESSION延期

// SESSION延期app.use((req, res, next) => {
req.session.nowInMinutes = Math.floor(Date.now() / 60e3);});

转载地址:http://wjclf.baihongyu.com/

你可能感兴趣的文章
【托业】【怪兽】TEST04
查看>>
【托业】【怪兽】TEST01
查看>>
【托业】【怪兽】TEST02
查看>>
【托业】【怪兽】TEST03
查看>>
【托业】【跨栏】TEST04
查看>>
【托业】【跨栏】TEST05
查看>>
【托业】【全真题库】TEST1-语法题+阅读题
查看>>
【托业】【全真题库】TEST3-语法题+阅读题
查看>>
【托业】【跨栏】TEST06
查看>>
【托业】320题-语法题(03-04)(03-37、04-11)
查看>>
【日语】五十音图-一个好用的日语五十音图记忆方法
查看>>
linux下使用crontab实现定时PHP计划任务失败的原因分析
查看>>
linux环境下安装redis扩展
查看>>
一个简单大方的赞后+1,踩后-1js动画效果
查看>>
新手学习编程的最佳方式
查看>>
从json_encode过来的的字符串被返回到html页面时的解析
查看>>
linux虚拟机局域网网卡配置
查看>>
用js判断是否为手机浏览,如果是手机浏览就跳转到手机站
查看>>
每天一个linux命令(26):用SecureCRT来上传和下载文件(转载自竹子)
查看>>
定时启动计划任务(转载自网络)
查看>>