您的位置:首页 > 数据库 > Mongodb

用NodeJS+Mongodb+Pug开发博客网站

2017-08-27 12:45 232 查看

使用NodeJS,mongodb和pug模板引擎开发个人博客网站,前端样式采用Bootstrap3.0实现,nodejs开发框架采用的是express4.0。

一:开发环境

需要安装node的高级版本,我使用的是node v8.1.2版本:



安装mongodb,我安装的是v3.4.5版本:



二:开发介绍

使用express实现工程初始化

先新建工程,使用express快速生成工程结构

npm install express-generator -g  //全局安装express生成器
express myblog //初始化工程
cd myblog //进入工程目录
npm install //开始安装依赖包
npm start //启动应用


ok,到处为止项目工程初始化完毕,可以打开浏览器输入:http://localhost:3000/ 会打开对应的界面,如下图所示



代表项目初始化成功。

安装对应的依赖包,如mongodb,bootstrap和pug等依赖库。

npm install --save-dev mongodb bootstrap pug


jade替换成pug

完成以上步骤以后我们可以开始正式进入开发了,由于express生成器默认采用的是jade模板引擎,我们需要先切换到pug上来,pug是jade的升级之后的新版本的名称。由于pug是jade的升级版本,所以它们的语法基本是兼容的,所以默认的话我们直接改名字就是了:



如图所示,直接改成pug就行了

文章编辑发送模块开发

OK,完成上述步骤以后我们开始进入最重要的文章编辑发送模块的开发了,毕竟这是一个博客网站,离不开编辑发送功能。富文本编辑器我选择的是UEditor,UEditor官网,大家也可以选择变得编辑器或者像csdn那样使用markdown,也可以自己开发一个富文本编辑器。

新建article_edit.pug,引入ueditor编辑器:

extends layout
block content
script(type="text/javascript" charset="utf-8" src="/ueditor/ueditor.config.js")
script(type="text/javascript" charset="utf-8" src="/ueditor/ueditor.all.js")
script(type="text/javascript" charset="utf-8" src="/ueditor/zh-cn.js")
....
div(class="container")
script(id="editor" type="text/plain" style="width:100%;height:800px;")


完成富文本编辑器的引入,这里要介绍将要使用到的两个获取编辑器内容的方法:

var articleContent      = UE.getEditor('editor').getContentTxt();//获取不带标签的内容
var articleContentHTML  = UE.getEditor('editor').getContent();//获取带html标签的内容


图片及文件的插入

上传图片需要使用后台的接口,上传成功以后需要返回对应的图片路径,然后通过img标签引入src显示在文本框中,有一点需要特别注意的就是上传图片的返回数据格式,要按下面这样:

{"url":"/uploadfiles/image/20170827/47651877.jpg","state":"SUCCESS"}


否则会认为返回失败,不能正常显示。

上传文件后台接口开发,代码如下所示:

router.post('/ueditors', (req, res, next) => {
const action = req.query.action;
const date   = new Date();
let addPath = "image";
if(action==="uploadvideo"){
addPath = "video";
}else if(action==="uploadfile"){
addPath = "file";
}
addPath += "/" + getYYYYMMDD();
const absolutePath = path.resolve(__dirname, '../ueditor/uploadfiles/'+addPath);
if(!fs.existsSync(absolutePath)){
fs.mkdirSync(absolutePath);
}

const form = new multiparty.Form({uploadDir: absolutePath});

form.parse(req, (err, fields, files) => {
// var filesTmp = JSON.stringify(files,null,2);
if(err){
res.writeHead(500, {'content-type': 'text/plain;charset=utf-8'});
res.end('upload error');
} else {
var inputFile = files.upfile[0];
var uploadedPath = inputFile.path;
const filename = hashCode(inputFile.originalFilename) + "." + getSuffixStr(inputFile.originalFilename);
var dstPath = absolutePath +"/"+ filename;
//重命名为真实文件名
fs.rename(uploadedPath, dstPath, function(err) {
if(err){
res.writeHead(501, {'content-type': 'text/plain;charset=utf-8'});
res.end('rename error');
} else {
res.status(200).json({"url" : "/uploadfiles/" + addPath + "/" + filename, "state":"SUCCESS"});
}
});
}
});
});


权限及文章管理

在这里我只设置了两个权限一个是管理员,一个是游客,管理员拥有对文章的发布,更新,下架和删除权限,其它人只有光看和评论的权限,所以在user的对象里增加一个role字段,用作权限区分。

新建user数据库表

const insertUserInfo = async (userinfo) => {
let isExists = false;
await  Promise.resolve(findUserNameState(userinfo.username)).then(result => {
if(result.status===1002){
isExists = true;
}
});

if(isExists){
return { status: 1002, message: '注册失败,用户名重复了!'}
}

await Promise.resolve(findEmailState(userinfo.email)).then(result => {
if(result.status===1003){
isExists = true;
}
});

if(isExists){
return { status: 1003, message: '注册失败,邮箱已被注册!'}
}

return MongoClient.connect(DB_CONN_STR)
.then(db => {
return _doInsertUserToDB(db, userinfo);
});
}

async function _doInsertUserToDB(db, userinfo) {
//连接到表 user
const collection = db.collection('user');

if(userinfo.username==='superman'){
userinfo.role = 10000;
}else{
userinfo.role = 100;
}
await collection.insert(userinfo);
db.close();
return { status: 1000, message: '注册成功!'};
}

const findUserNameState = (username) => {
return MongoClient.connect(DB_CONN_STR)
.then(db => {
return _doFindUserNameState(db, username);
});
}

async function _doFindUserNameState(db, username) {
const collection = db.collection('user');
const whereStr = { "username" : username };
let resultDocument;
await collection.findOne(whereStr).then(document => {
resultDocument = document;
});
db.close();
if(resultDocument){
return { status: 1002, message: '用户名重复了!' }
}
return { status: 1005 };
}


在插入数据之前进行判断,判断用户名是否存在,如果存在则不能重复注册

- 权限控制

/**
* [description] 首页用户权限验证,判断用户是否有写文章的权限
* @param  {[type]} (req, res,          next [description]
* @return {[type]}       [description]
*/
router.use((req, res, next) => {
if(req.cookies&&req.cookies.username){
const username = req.cookies.username;
queryUserInfoByUserName(username).then(document => {
let navMenuObj;
if(document&&document.role===10000){
navMenuObj = Array.from(NAV_MENU_ARRAY);
navMenuObj.push({value: '写文章', path: '/article/publish'});
}
if(!navMenuObj){
navMenuObj = NAV_MENU_ARRAY;
}
req.menu_array = navMenuObj;
next();
});
}else{
next();
}
});

/* GET home page. */
router.get('/', (req, res, next) => {
res.redirect('/index.html');
});

router.get('/index.html', (req, res, next) => {
const menu_array = req.menu_array || NAV_MENU_ARRAY;
Promise.all([queryArticleCount(), queryAllArticleByCategoryPage(1)])
.then(array => {
const totalsize = array[0];
const result    = array[1];
res.render('index', optData(menu_array, result.status===1000?result.articleArray:[], 1, totalsize));
});
});


通过cookie判断用户的权限,做到菜单显示的动态控制,只有管理员才有写文章的功能。

评论模块开发

接下来介绍评论模块的开发,首先明确一点,评论是跟着文章走的,我这边没有要求用户一定要登录才可以评论,所以对评论表结构的设计也就简单化了。

新建comments.pug,评论列表

div(class="comments" style="padding: 1em;")
if comments
h4 评论 (#{comments.length} 条)
else
h4 评论
- var index = -1;
each comment in comments
- var date = new Date(comment.time);
- index++;
div(class='comments-info')
div(class='cmnt-icon-left')
a(href='#') #[img(src='/images/icon3.png')]
div(class='cmnt-icon-right')
p #{date.getFullYear()}年#{date.getMonth()+1}月#{date.getDate()}日  #{date.getHours()}:#{date.getMinutes()}
p #[a(href='#') #{comment.nickname}]
p(class='cmmnt') !{comment.content}
div(class='clearfix')
div(class='aliqua')
a(href='javascript:void(0)' data-index=""+index
onclick="blogModel.rebackComment()") 引用


新建comments.pug,发表评论

div(class="consequat" style="padding: 1em;")
h4 发表评论
form(onsubmit="return false")
input(type="text"  id="username" placeholder="请输入您的称呼" required="true" pattern="^\\S{2,}")
input(type="email" id="email"    placeholder="电子邮箱"       required="true")
input(type="url"   id="subject"  placeholder="个人主页"       required="true")
textarea(type="text" id="content" required="true" placeholder="请填写评论内容")
button(class="btn btn-primary" type="submit" id="submitBtn" onclick='blogModel.sendComment()') 发表评论


评论表操作,comment_dao.js

const insertComment = (commentObj) => {
return MongoClient.connect(DB_CONN_STR)
.then(db => {
return _doInsertComment(db, commentObj);
})
.catch(error => {
return Promise.resolve({status: 1001,message: error.message});
});
}

async function _doInsertComment(db, commentObj) {
const collection = db.collection("comment");
let   insertObj;
await collection.insertOne(commentObj).then(result => {
insertObj = result;
});
db.close();
if(insertObj&&insertObj.insertedCount===1){
return {status: 1000}
}
return {status:1001, message: 'insert error'};
}

const queryCommentByArticleId = (articleId) => {
return MongoClient.connect(DB_CONN_STR)
.then(db => {
return _doQueryCommentByArticleId(db, articleId);
})
.catch(error => {
return Promise.resolve({status: 1001,message: error.message});
});
}

async function _doQueryCommentByArticleId(db, articleId) {
const collection = db.collection("comment");
const whereStr = { "articleid" : articleId };
let comments;
await collection.find(whereStr).toArray()
.then(resultArray => {
comments = resultArray;
});
db.close();
return {status: 1000, comments };
}

module.exports = {
insertComment,
queryCommentByArticleId
}


二:效果展示

登录与注册界面







发表文章界面



文章列表界面



文章详情



评论





三:源码下载

下载地址: blog源码下载地址



支持源码付费下载,支付完成之后,请发生邮件到269786271@qq.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息