美文网首页
Node + MongoDB 建站 2期 -1

Node + MongoDB 建站 2期 -1

作者: LiuliuZhang | 来源:发表于2018-04-20 10:05 被阅读0次

Grant

npm install grunt grant-cli -g全局安装grant及grant命令行工具,

npm install grunt-contrib-watch --save-dev  //文件修改重新执行注册的任务
npm install grunt-nodemon --save-dev    //监听app.js修改
npm install grunt-concurrent --save-dev //慢任务如sass

建立grunt.js文件

module.exports = function(grunt) {

  grunt.initConfig({
    watch: {
      jade: {
        files: ['views/**'],
        options: {
          livereload: true
        }
      },
      js: {
        files: ['public/js/**', 'models/**/*.js', 'schemas/**/*.js'],
        //tasks: ['jshint'],
        options: {
          livereload: true
        }
      }
    },

    nodemon: {
      dev: {
        options: {
          file: 'app.js',
          args: [],
          ignoredFiles: ['README.md', 'node_modules/**', '.DS_Store'],
          watchedExtensions: ['js'],
          watchedFolders: ['app','config'],
          debug: true,
          delayTime: 1,
          env: {
            PORT: 3000
          },
          cwd: __dirname
        }
      }
    },

    concurrent: {
      tasks: ['nodemon', 'watch'],
      options: {
        logConcurrentOutput: true
      }
    }
  })

  grunt.loadNpmTasks('grunt-contrib-watch')
  grunt.loadNpmTasks('grunt-nodemon')
  grunt.loadNpmTasks('grunt-concurrent')
  grunt.option('force', true)  //避免一些错误影响后续执行

  grunt.registerTask('default', ['concurrent'])  //默认任务

}

根目录下执行grunt命令启动

用户登陆注册

1)用户模型及密码处理

安装bcrypt进行密码加盐处理 npm install bcrypt --save-dev,创建schema user.js

var mongoose = require('mongoose')
var bcrypt = require('bcrypt')
var SALT_WORK_FACTOR = 10

var UserSchema = new mongoose.Schema({
  name: {
    unique: true,
    type: String
  },
  password: String,
  meta: {
    createAt: {
      type: Date,
      default: Date.now()
    },
    updateAt: {
      type: Date,
      default: Date.now()
    }
  }
})

UserSchema.pre('save', function(next) {
  var user = this

  if (this.isNew) {
    this.meta.createAt = this.meta.updateAt = Date.now()
  }
  else {
    this.meta.updateAt = Date.now()
  }

  bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
    if (err) return next(err)

    bcrypt.hash(user.password, salt, function(err, hash) {
      if (err) return next(err)

      user.password = hash
      next()
    })
  })
})

UserSchema.statics = {
  fetch: function(cb) {
    return this
      .find({})
      .sort('meta.updateAt')
      .exec(cb)
  },
  findById: function(id, cb) {
    return this
      .findOne({_id: id})
      .exec(cb)
  }
}

module.exports = UserSchema

2)登录注册前端视图

修改header.jade,添加navbar,添加注册与登陆两个按钮,分别为其添加模态视图

.container
  .row
    .page-header
      h1= title
      small 重度科幻迷

.navbar.navbar-default.navbar-fixed-bottom
  .container
    .navbar-header
      a.navbar-brand(href="/") 重度科幻迷
    if user
      p.navbar-text.navbar-right
        span 欢迎您,#{user.name}
        span  | 
        a.navbar-link(href="/logout") 登出
    else
      p.navbar-text.navbar-right
        a.navbar-link(href="#", data-toggle="modal", data-target="#signupModal") 注册
        span  | 
        a.navbar-link(href="#", data-toggle="modal", data-target="#signinModal") 登录
#signupModal.modal.fade
  .modal-dialog
    .modal-content
      form(method="POST", action="/user/signup")
        .modal-header 注册
        .modal-body
          .form-group
            label(for="signupName") 用户名
            input#signupName.form-control(name="user[name]", type="text")
          .form-group
            label(for="signupPassword") 密码
            input#signupPassword.form-control(name="user[password]", type="text")
        .modal-footer
          button.btn.btn-default(type="button", data-dismiss="modal") 关闭
          button.btn.btn-success(type="submit") 提交
#signinModal.modal.fade
  .modal-dialog
    .modal-content
      form(method="POST", action="/user/signin")
        .modal-header 登录
        .modal-body
          .form-group
            label(for="signinName") 用户名
            input#signinName.form-control(name="user[name]", type="text")
          .form-group
            label(for="signinPassword") 密码
            input#signinPassword.form-control(name="user[password]", type="text")
        .modal-footer
          button.btn.btn-default(type="button", data-dismiss="modal") 关闭
          button.btn.btn-success(type="submit") 提交

3)注册用户后台存储

添加Model user.js

var mongoose = require('mongoose')
var UserSchema = require('../schemas/user')
var User = mongoose.model('User', UserSchema)

module.exports = User

在app.js中引入user model,并添加路由

var User = require('./models/user')
......
app.post('/user/signup',function(req,res){
    var _user = req.body.user
    var user = new User(_user)

    user.save(function(err,user){
        if (err) {
            console.log(err)
        }
        res.redirect('/')
    })
})

添加用户列表页

extends ../layout

block content
  .container
    .row
      table.table.table-hover.table-bordered
        thead
          tr
            th 名字
            th 时间
            th 查看
            th 修改
            th 删除
        tbody
          each item in users
            tr(class="item-id-#{item._id}")
              td #{item.name}
              td #{moment(item.meta.updateAt).format('MM/DD/YYYY')}
              td: a(target="_blank", href="../movie/#{item._id}") 查看
              td: a(target="_blank", href="../admin/update/#{item._id}") 修改
              td
                button.btn.btn-danger.del(type="button", data-id="#{item._id}") 删除

在app.js中添加路由

//user list page
app.get('/admin/userlist', function(req, res) {
    Movie.fetch(function(err,users){
        if (err) {
            console.log(err)
        }
        res.render('userlist', {
            title: 'imooc 用户列表页',
            users: users
        })
    })
})

登陆时判断用户是否存在,更改app.js signup路由

app.post('/user/signup',function(req,res){
    var _user = req.body.user

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (user) {
            return res.redirect('/')
        } else {
            var user = new User(_user)

            user.save(function(err,user){
                if (err) {
                    console.log(err)
                }        
                res.redirect('/admin/userlist')
            })
        }
    })
})

4)实现登陆逻辑

添加signin路由

app.post('/user/signin',function(req,res){
    var _user = req.body.user
    var name = _user.name
    var password = _user.password

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (!user) {
            return res.redirect('/')
        } 
        user.comparePassword(password, function(err, isMatch){
            if (err) {
                console.log(err)
            }
            if (isMatch){
                console.log('Password is match')
                return res.redirect('/')
            } else {
                console.log('Password not match')
            }
        })
    })
})

在user schema中添加comparePassword方法

UserSchema.methods = {
  comparePassword: function(_password, cb) {
    bcrypt.compare(_password, this.password, function(err, isMatch) {
      if (err) return cb(err)
      cb(null, isMatch)
    })
  }
}

5)保持用户状态

在app.js中安装并引用cookie-session,index中打印当前user,在/user/signin的post中,如果密码匹配,存储user到session

var cookieSession = require('cookie-session')
app.use(cookieSession({
    secret: 'imooc'
}))
......
//index page
app.get('/', function(req, res) {
    console.log('user in session:')
    console.log(req.session.user)
......

if (isMatch){
                console.log('Password is match')
                req.session.user = user
                return res.redirect('/')
            } 

6)利用MongoDB做会话的持久化

安装 express-session connect-mongo

var session = require('express-session')
var MongoStore = require('connect-mongo')(session);

app.use(session({
    secret: 'imooc',
    store: new MongoStore({
        url:'mongodb://localhost/imooc',
        collection: 'sessions'
    })
}))

7)注销功能实现

header.jade中修改,判断user显示

    if user
      p.navbar-text.navbar-right
        span 欢迎您,#{user.name}
        span  | 
        a.navbar-link(href="/logout") 登出
    else
      p.navbar-text.navbar-right
        a.navbar-link(href="#", data-toggle="modal", data-target="#signupModal") 注册
        span  | 
        a.navbar-link(href="#", data-toggle="modal", data-target="#signinModal") 登录

添加登出路由,在index路由中,赋予locals user值

    console.log('user in session:')
    console.log(req.session.user)
    var _user = req.session.user
    if(_user) {
        app.locals.user = _user
    }
    Movie.fetch(function(err,movies){
......
app.get('/logout',function(req,res){
    delete req.session.user
    delete app.locals.user  
    res.redirect('/')
})

8)会话持久逻辑预处理

将index路由中的赋值移到预处理逻辑中

app.use(function(req,res,next){
    var _user = req.session.user
    if(_user) {
        app.locals.user = _user
    }
    return next()
})

9)调整目录,独立路由

添加config/routes.js,将路由相关代码移到该文件

var _ = require('underscore')
var Movie = require('../models/movie')
var User = require('../models/user')

module.exports = function(app){

    app.use(function(req,res,next){
        var _user = req.session.user
        if(_user) {
            app.locals.user = _user
        }
        return next()
    })
//index page
app.get('/', function(req, res) {
    console.log('user in session:')
    console.log(req.session.user)

    Movie.fetch(function(err,movies){
        if (err) {
            console.log(err)
        }

        res.render('index', {
            title: 'imooc 首页',
            movies: movies
        })
    })

})

app.post('/user/signup',function(req,res){
    var _user = req.body.user

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (user) {
            return res.redirect('/')
        } else {
            var user = new User(_user)

            user.save(function(err,user){
                if (err) {
                    console.log(err)
                }
        
                res.redirect('/admin/userlist')
            })
        }
    })

})

app.post('/user/signin',function(req,res){
    var _user = req.body.user
    var name = _user.name
    var password = _user.password

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (!user) {
            return res.redirect('/')
        } 
        user.comparePassword(password, function(err, isMatch){
            if (err) {
                console.log(err)
            }
            if (isMatch){
                console.log('Password is match')
                req.session.user = user
                return res.redirect('/')
            } else {
                console.log('Password not match')
            }
        })
    })

})

app.get('/logout',function(req,res){
    delete req.session.user
    delete app.locals.user  
    res.redirect('/')
})

//user list page
app.get('/admin/userlist', function(req, res) {
    User.fetch(function(err,users){
        if (err) {
            console.log(err)
        }

        res.render('userlist', {
            title: 'imooc 用户列表页',
            users: users
        })
    })
})


//detail page
app.get('/movie/:id', function(req, res) {
    var id = req.params.id
    Movie.findById(id,function(err,movie){
        res.render('detail', {
            title: 'imooc 详情页',
            movie: movie
        })
    })

})

//admin page
app.get('/admin/movie', function(req, res) {
    res.render('admin', {
        title: 'imooc 后台录入页',
        movie: {
            title: '',
            doctor: '',
            country: '',
            year: '',
            poster: '',
            flash: '',
            summary: '',
            language: ''
        }
    })
})

//admin update movie
app.get('/admin/update/:id',function(req,res){
    var id = req.params.id

    if (id) {
        Movie.findById(id,function(err,movie){
            res.render('admin',{
                title: 'Imooc 后台更新页',
                movie: movie
            })
        })
    }
})

//admin post movie
app.post('/admin/movie/new', function(req,res){
    var id = req.body.movie._id
    var movieObj = req.body.movie
    var _movie

    if (id !=='undefined') {
        Movie.findById(id, function(err,movie){
            if (err){
                console.log(err)
            }

            _movie = _.extend(movie, movieObj)
            _movie.save(function(err,movie) {
                if (err){
                    console.log(err)
                }
                res.redirect('/movie/'+movie._id)
            })
        })
    } else {
        _movie = new Movie({
            dector: movieObj.dector,
            title: movieObj.title,
            country: movieObj.country,
            language: movieObj.language,
            year: movieObj.year,
            poster: movieObj.poster,
            summary: movieObj.summary,
            flash: movieObj.flash,
        })

        _movie.save(function(err,movie) {
            if (err){
                console.log(err)
            }
            res.redirect('/movie/'+movie._id)
        })
    }
})

//list page
app.get('/admin/list', function(req, res) {
    Movie.fetch(function(err,movies){
        if (err) {
            console.log(err)
        }

        res.render('list', {
            title: 'imooc 列表页',
            movies: movies
        })
    })
})

//list delete movie
app.delete('/admin/list',function(req,res){
    var id = req.query.id

    if (id) {
        Movie.remove({_id: id},function(err,movie){
            if (err) {
                console.log(err)
            } else {
                res.json({success: 1})
            }
        })
    }
})
}

app.js中引入routes文件

app.use(session({
    secret: 'imooc',
    store: new MongoStore({
        url:'mongodb://localhost/imooc',
        collection: 'sessions'
    })
}))

require('./config/routes')(app)

10)配置入口文件

安装morgan,

var logger = require('morgan')

if('development' === app.get('env')){
    app.set('showStackError', true)
    app.use(logger(':method :url :status'))
    app.locals.pretty = true
    mongoose.set('debug',true)
}

11)调整目录,分离mvc

新建app文件夹,将models/schemas/views文件夹移到该文件夹,添加controllers文件夹,将routes中代码移到新建的文件中
index.js

var Movie = require('../models/movie')

exports.index = function(req, res) {
    console.log('user in session:')
    console.log(req.session.user)

    Movie.fetch(function(err,movies){
        if (err) {
            console.log(err)
        }
        res.render('index', {
            title: 'imooc 首页',
            movies: movies
        })
    })
}

user.js

var User = require('../models/user')


exports.signup = function(req,res){
    var _user = req.body.user

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (user) {
            return res.redirect('/')
        } else {
            var user = new User(_user)

            user.save(function(err,user){
                if (err) {
                    console.log(err)
                }
        
                res.redirect('/admin/userlist')
            })
        }
    })

}

exports.signin = function(req,res){
    var _user = req.body.user
    var name = _user.name
    var password = _user.password

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (!user) {
            return res.redirect('/')
        } 
        user.comparePassword(password, function(err, isMatch){
            if (err) {
                console.log(err)
            }
            if (isMatch){
                console.log('Password is match')
                req.session.user = user
                return res.redirect('/')
            } else {
                console.log('Password not match')
            }
        })
    })

}

exports.logout = function(req,res){
    delete req.session.user
    // delete app.locals.user  
    res.redirect('/')
}

//user list page
exports.list = function(req, res) {
    User.fetch(function(err,users){
        if (err) {
            console.log(err)
        }

        res.render('userlist', {
            title: 'imooc 用户列表页',
            users: users
        })
    })
}

movie.js

var _ = require('underscore')
var Movie = require('../models/movie')

//detail page
exports.detail = function(req, res) {
    var id = req.params.id
    Movie.findById(id,function(err,movie){
        res.render('detail', {
            title: 'imooc 详情页',
            movie: movie
        })
    })

}

//admin page
exports.new =  function(req, res) {
    res.render('admin', {
        title: 'imooc 后台录入页',
        movie: {
            title: '',
            doctor: '',
            country: '',
            year: '',
            poster: '',
            flash: '',
            summary: '',
            language: ''
        }
    })
}

//admin update movie
exports.update = function(req,res){
    var id = req.params.id

    if (id) {
        Movie.findById(id,function(err,movie){
            res.render('admin',{
                title: 'Imooc 后台更新页',
                movie: movie
            })
        })
    }
}

//admin post movie
exports.save =  function(req,res){
    var id = req.body.movie._id
    var movieObj = req.body.movie
    var _movie

    if (id !=='undefined') {
        Movie.findById(id, function(err,movie){
            if (err){
                console.log(err)
            }

            _movie = _.extend(movie, movieObj)
            _movie.save(function(err,movie) {
                if (err){
                    console.log(err)
                }
                res.redirect('/movie/'+movie._id)
            })
        })
    } else {
        _movie = new Movie({
            dector: movieObj.dector,
            title: movieObj.title,
            country: movieObj.country,
            language: movieObj.language,
            year: movieObj.year,
            poster: movieObj.poster,
            summary: movieObj.summary,
            flash: movieObj.flash,
        })

        _movie.save(function(err,movie) {
            if (err){
                console.log(err)
            }
            res.redirect('/movie/'+movie._id)
        })
    }
}

//list page
exports.list =  function(req, res) {
    Movie.fetch(function(err,movies){
        if (err) {
            console.log(err)
        }

        res.render('list', {
            title: 'imooc 列表页',
            movies: movies
        })
    })
}

//list delete movie
exports.del = function(req,res){
    var id = req.query.id

    if (id) {
        Movie.remove({_id: id},function(err,movie){
            if (err) {
                console.log(err)
            } else {
                res.json({success: 1})
            }
        })
    }
}

routes.js修改为如下

var Index = require('../app/controllers/index')
var User = require('../app/controllers/user')
var Movie = require('../app/controllers/movie')

module.exports = function(app){

    app.use(function(req,res,next){
        var _user = req.session.user
        // if(_user) {
            app.locals.user = _user
        // }
        next()
    })
//index page
app.get('/', Index.index)
//user
app.post('/user/signup',User.signup)
app.post('/user/signin',User.signin)
app.get('/logout',User.logout)
app.get('/admin/userlist', User.list)

//movie
app.get('/movie/:id', Movie.detail)
app.get('/admin/movie', Movie.new)
app.get('/admin/update/:id',Movie.update)
app.post('/admin/movie/new', Movie.save)
app.get('/admin/list', Movie.list)
app.delete('/admin/list',Movie.del)
}

修改app.js文件中路径app.set('views', './app/views/pages')

12)添加注册登陆跳转页面

添加signin与signup jade文件

extends ../layout

block content
  .container
    .row
      .col-md-5
        form(method="POST", action="/user/signin")
          .modal-body
            .form-group
              label(for="signinName") 用户名
              input#signinName.form-control(name="user[name]", type="text")
            .form-group
              label(for="signinPassword") 密码
              input#signinPassword.form-control(name="user[password]", type="text")
          .modal-footer
            button.btn.btn-default(type="button", data-dismiss="modal") 关闭
            button.btn.btn-success(type="submit") 提交

添加路由

app.get('/signin', User.showSignin)
app.get('/signup', User.showSignup)

添加controller方法,修改signup signin重定向地址

// signup
exports.showSignup = function(req, res) {
    res.render('signup', {
      title: '注册页面'
    })
  }
  
exports.showSignin = function(req, res) {
    res.render('signin', {
      title: '登录页面'
    })
  }
exports.signup = function(req,res){
    var _user = req.body.user

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (user) {
            return res.redirect('/signin')
        } else {
            var user = new User(_user)

            user.save(function(err,user){
                if (err) {
                    console.log(err)
                }
        
                res.redirect('/')
            })
        }
    })

}

exports.signin = function(req,res){
    var _user = req.body.user
    var name = _user.name
    var password = _user.password

    User.findOne({name:_user.name}, function(err,user){
        if (err) {
            console.log(err)
        }
        if (!user) {
            return res.redirect('/signup')
        } 
        user.comparePassword(password, function(err, isMatch){
            if (err) {
                console.log(err)
            }
            if (isMatch){
                console.log('Password is match')
                req.session.user = user
                return res.redirect('/')
            } else {
                return res.redirect('/signin')
                console.log('Password not match')
            }
        })
    })
}

13)用户权限管理

在user schema中添加role字段

  password: String,
  // 0: nomal user
  // 1: verified user
  // 2: professonal user
  // >10: admin
  // >50: super admin
  role: {
    type: Number,
    default: 0
  },

在routes.js文件中,访问userlist添加参数 app.get('/admin/userlist', User.signinRequired, User.adminRequired, User.list),在user controller中添加方法,如果没有登陆则调到signin,如果没有权限则调到signin

// midware for user
exports.signinRequired = function(req, res, next) {
  var user = req.session.user
  if (!user) {
    return res.redirect('/signin')
  }
  next()
}

exports.adminRequired = function(req, res, next) {
  var user = req.session.user
  if (user.role <= 10) {
    return res.redirect('/signin')
  }
  next()
}

给其他路由也加上admin权限管理

//index page
app.get('/', Index.index)
//user
app.post('/user/signup',User.signup)
app.post('/user/signin',User.signin)
app.get('/signin', User.showSignin)
app.get('/signup', User.showSignup)
app.get('/logout',User.logout)
app.get('/admin/userlist', User.signinRequired, User.adminRequired, User.list)

//movie
app.get('/movie/:id', Movie.detail)
app.get('/admin/movie', User.signinRequired, User.adminRequired, Movie.new)
app.get('/admin/update/:id',User.signinRequired, User.adminRequired, Movie.update)
app.post('/admin/movie/new',User.signinRequired, User.adminRequired, Movie.save)
app.get('/admin/list',User.signinRequired, User.adminRequired, Movie.list)
app.delete('/admin/list',User.signinRequired, User.adminRequired, Movie.del)

相关文章

网友评论

      本文标题:Node + MongoDB 建站 2期 -1

      本文链接:https://www.haomeiwen.com/subject/ggqckftx.html