Node + MongoDB 建站 2期 -1

Grant

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

npm install grunt-contrib-watch --save-dev  //文件修改重新執(zhí)行注冊(cè)的任務(wù)
npm install grunt-nodemon --save-dev    //監(jiān)聽app.js修改
npm install grunt-concurrent --save-dev //慢任務(wù)如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)  //避免一些錯(cuò)誤影響后續(xù)執(zhí)行

  grunt.registerTask('default', ['concurrent'])  //默認(rèn)任務(wù)

}

根目錄下執(zhí)行grunt命令啟動(dòng)

用戶登陸注冊(cè)

1)用戶模型及密碼處理

安裝bcrypt進(jìn)行密碼加鹽處理 npm install bcrypt --save-dev,創(chuàng)建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)登錄注冊(cè)前端視圖

修改header.jade,添加navbar,添加注冊(cè)與登陸兩個(gè)按鈕,分別為其添加模態(tài)視圖

.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") 注冊(cè)
        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 注冊(cè)
        .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") 關(guān)閉
          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") 關(guān)閉
          button.btn.btn-success(type="submit") 提交

3)注冊(cè)用戶后臺(tái)存儲(chǔ)

添加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('/')
    })
})

添加用戶列表頁(yè)

extends ../layout

block content
  .container
    .row
      table.table.table-hover.table-bordered
        thead
          tr
            th 名字
            th 時(shí)間
            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 用戶列表頁(yè)',
            users: users
        })
    })
})

登陸時(shí)判斷用戶是否存在,更改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)實(shí)現(xiàn)登陸邏輯

添加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)保持用戶狀態(tài)

在app.js中安裝并引用cookie-session,index中打印當(dāng)前user,在/user/signin的post中,如果密碼匹配,存儲(chǔ)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做會(huì)話的持久化

安裝 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)注銷功能實(shí)現(xiàn)

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") 注冊(cè)
        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)會(huì)話持久邏輯預(yù)處理

將index路由中的賦值移到預(yù)處理邏輯中

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

9)調(diào)整目錄,獨(dú)立路由

添加config/routes.js,將路由相關(guān)代碼移到該文件

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 首頁(yè)',
            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 用戶列表頁(yè)',
            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 詳情頁(yè)',
            movie: movie
        })
    })

})

//admin page
app.get('/admin/movie', function(req, res) {
    res.render('admin', {
        title: 'imooc 后臺(tái)錄入頁(yè)',
        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 后臺(tái)更新頁(yè)',
                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 列表頁(yè)',
            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)調(diào)整目錄,分離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 首頁(yè)',
            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 用戶列表頁(yè)',
            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 詳情頁(yè)',
            movie: movie
        })
    })

}

//admin page
exports.new =  function(req, res) {
    res.render('admin', {
        title: 'imooc 后臺(tái)錄入頁(yè)',
        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 后臺(tái)更新頁(yè)',
                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 列表頁(yè)',
            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)添加注冊(cè)登陸跳轉(zhuǎn)頁(yè)面

添加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") 關(guān)閉
            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: '注冊(cè)頁(yè)面'
    })
  }
  
exports.showSignin = function(req, res) {
    res.render('signin', {
      title: '登錄頁(yè)面'
    })
  }
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)用戶權(quán)限管理

在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添加參數(shù) app.get('/admin/userlist', User.signinRequired, User.adminRequired, User.list),在user controller中添加方法,如果沒有登陸則調(diào)到signin,如果沒有權(quán)限則調(diào)到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權(quán)限管理

//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)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容