From d1c029b55bedc751e4aca4716f7c557c62c8075c Mon Sep 17 00:00:00 2001 From: Alex Young Date: Mon, 7 Feb 2011 11:19:15 +0000 Subject: [PATCH] Updated Mongoose to 1.0.7 --- app.js | 75 ++++++------ models.js | 192 +++++++++++++++--------------- package.json | 2 +- public/javascripts/application.js | 24 +++- test/app.test.js | 8 +- test/helper.js | 2 +- views/layout.jade | 2 +- 7 files changed, 166 insertions(+), 139 deletions(-) diff --git a/app.js b/app.js index dfc33cd..2ac3218 100644 --- a/app.js +++ b/app.js @@ -2,10 +2,11 @@ var express = require('express@1.0.0'), connect = require('connect@0.5.1'), jade = require('jade@0.6.0'), app = module.exports = express.createServer(), - mongoose = require('mongoose@0.0.4').Mongoose, + mongoose = require('mongoose@1.0.7'), mongoStore = require('connect-mongodb@0.1.1'), markdown = require('markdown').markdown, sys = require('sys'), + models = require('./models'), db, Document, User, @@ -28,8 +29,6 @@ app.configure('production', function() { app.set('db-uri', 'mongodb://localhost/nodepad-production'); }); -db = mongoose.connect(app.set('db-uri')); - app.configure(function() { app.set('views', __dirname + '/views'); app.use(express.favicon()); @@ -42,23 +41,25 @@ app.configure(function() { app.use(express.staticProvider(__dirname + '/public')); }); -app.Document = Document = require('./models.js').Document(db); -app.User = User = require('./models.js').User(db); -app.LoginToken = LoginToken = require('./models.js').LoginToken(db); +models.defineModels(mongoose, function() { + app.Document = Document = mongoose.model('Document'); + app.User = User = mongoose.model('User'); + app.LoginToken = LoginToken = mongoose.model('LoginToken'); + db = mongoose.connect(app.set('db-uri')); +}) function authenticateFromLoginToken(req, res, next) { var cookie = JSON.parse(req.cookies.logintoken); - LoginToken.find({ email: cookie.email, - series: cookie.series, - token: cookie.token }) - .first(function(token) { + LoginToken.findOne({ email: cookie.email, + series: cookie.series, + token: cookie.token }, (function(err, token) { if (!token) { res.redirect('/sessions/new'); return; } - User.find({ email: token.email }).first(function(user) { + User.findOne({ email: token.email }, function(err, user) { if (user) { req.session.user_id = user.id; req.currentUser = user; @@ -72,12 +73,12 @@ function authenticateFromLoginToken(req, res, next) { res.redirect('/sessions/new'); } }); - }); + })); } function loadUser(req, res, next) { if (req.session.user_id) { - User.findById(req.session.user_id, function(user) { + User.findById(req.session.user_id, function(err, user) { if (user) { req.currentUser = user; next(); @@ -136,11 +137,11 @@ app.error(function(err, req, res) { // Document list app.get('/documents.:format?', loadUser, function(req, res) { - Document.find().all(function(documents) { + Document.find({}, function(err, documents) { switch (req.params.format) { case 'json': res.send(documents.map(function(d) { - return d.__doc; + return d.toObject(); })); break; @@ -153,7 +154,7 @@ app.get('/documents.:format?', loadUser, function(req, res) { }); app.get('/documents/:id.:format?/edit', loadUser, function(req, res, next) { - Document.findById(req.params.id, function(d) { + Document.findById(req.params.id, function(err, d) { if (!d) return next(new NotFound('Document not found')); res.render('documents/edit.jade', { locals: { d: d, currentUser: req.currentUser } @@ -173,7 +174,7 @@ app.post('/documents.:format?', loadUser, function(req, res) { d.save(function() { switch (req.params.format) { case 'json': - res.send(d.__doc); + res.send(d.toObject()); break; default: @@ -185,12 +186,12 @@ app.post('/documents.:format?', loadUser, function(req, res) { // Read document app.get('/documents/:id.:format?', loadUser, function(req, res, next) { - Document.findById(req.params.id, function(d) { + Document.findById(req.params.id, function(err, d) { if (!d) return next(new NotFound('Document not found')); switch (req.params.format) { case 'json': - res.send(d.__doc); + res.send(d.toObject()); break; case 'html': @@ -207,15 +208,16 @@ app.get('/documents/:id.:format?', loadUser, function(req, res, next) { // Update document app.put('/documents/:id.:format?', loadUser, function(req, res, next) { - Document.findById(req.body.d.id, function(d) { + Document.findById(req.body.d.id, function(err, d) { if (!d) return next(new NotFound('Document not found')); d.title = req.body.d.title; d.data = req.body.d.data; - d.save(function() { + + d.save(function(err) { switch (req.params.format) { case 'json': - res.send(d.__doc); + res.send(d.toObject()); break; default: @@ -228,7 +230,7 @@ app.put('/documents/:id.:format?', loadUser, function(req, res, next) { // Delete document app.del('/documents/:id.:format?', loadUser, function(req, res, next) { - Document.findById(req.params.id, function(d) { + Document.findById(req.params.id, function(err, d) { if (!d) return next(new NotFound('Document not found')); d.remove(function() { @@ -255,27 +257,28 @@ app.get('/users/new', function(req, res) { app.post('/users.:format?', function(req, res) { var user = new User(req.body.user); - function userSaved() { + function userSaveFailed() { + req.flash('error', 'Account creation failed'); + res.render('users/new.jade', { + locals: { user: user } + }); + } + + user.save(function(err) { + console.log(err); + if (err) return userSaveFailed(); + req.flash('info', 'Your account has been created'); switch (req.params.format) { case 'json': - res.send(user.__doc); + res.send(user.toObject()); break; default: req.session.user_id = user.id; res.redirect('/documents'); } - } - - function userSaveFailed() { - req.flash('error', 'Account creation failed'); - res.render('users/new.jade', { - locals: { user: user } - }); - } - - user.save(userSaved, userSaveFailed); + }); }); // Sessions @@ -286,7 +289,7 @@ app.get('/sessions/new', function(req, res) { }); app.post('/sessions', function(req, res) { - User.find({ email: req.body.user.email }).first(function(user) { + User.findOne({ email: req.body.user.email }, function(err, user) { if (user && user.authenticate(req.body.user.password)) { req.session.user_id = user.id; diff --git a/models.js b/models.js index 539d9a1..3bc49c2 100644 --- a/models.js +++ b/models.js @@ -1,115 +1,111 @@ -var mongoose = require('mongoose@0.0.4').Mongoose, - crypto = require('crypto'); - -mongoose.model('Document', { - properties: ['title', 'data', 'tags', 'user_id'], - - indexes: [ - 'title', - 'user_id' - ], - - getters: { - id: function() { +var crypto = require('crypto'), + Document, + User, + LoginToken; + +function defineModels(mongoose, fn) { + var Schema = mongoose.Schema, + ObjectId = Schema.ObjectId; + + /** + * Model: Document + */ + Document = new Schema({ + 'title': { type: String, index: true }, + 'data': String, + 'tags': [String], + 'user_id': ObjectId + }); + + Document.virtual('id') + .get(function() { return this._id.toHexString(); - } - } -}); + }); -mongoose.model('User', { - properties: ['email', 'hashed_password', 'salt'], + /** + * Model: User + */ + function validatePresenceOf(value) { + return value && value.length; + } - indexes: [ - [{ email: 1 }, { unique: true }] - ], + User = new Schema({ + 'email': { type: String, validate: [validatePresenceOf, 'an email is required'], index: { unique: true } }, + 'hashed_password': String, + 'salt': String + }); - getters: { - id: function() { + User.virtual('id') + .get(function() { return this._id.toHexString(); - }, - - password: function() { return this._password; } - }, + }); - setters: { - password: function(password) { + User.virtual('password') + .set(function(password) { this._password = password; this.salt = this.makeSalt(); this.hashed_password = this.encryptPassword(password); + }) + .get(function() { return this._password; }); + + User.method('authenticate', function(plainText) { + return this.encryptPassword(plainText) === this.hashed_password; + }); + + User.method('makeSalt', function() { + return Math.round((new Date().valueOf() * Math.random())) + ''; + }); + + User.method('encryptPassword', function(password) { + return crypto.createHmac('sha1', this.salt).update(password).digest('hex'); + }); + + User.pre('save', function(next) { + if (!validatePresenceOf(this.password)) { + next(new Error('Invalid password')); + } else { + next(); } - }, - - methods: { - authenticate: function(plainText) { - return this.encryptPassword(plainText) === this.hashed_password; - }, - - makeSalt: function() { - return Math.round((new Date().valueOf() * Math.random())) + ''; - }, - - encryptPassword: function(password) { - return crypto.createHmac('sha1', this.salt).update(password).digest('hex'); - }, - - isValid: function() { - // TODO: Better validation - return this.email && this.email.length > 0 && this.email.length < 255 - && this.password && this.password.length > 0 && this.password.length < 255; - }, - - save: function(okFn, failedFn) { - if (this.isValid()) { - this.__super__(okFn); - } else { - failedFn(); - } - } - } -}); - -mongoose.model('LoginToken', { - properties: ['email', 'series', 'token'], - - indexes: [ - 'email', - 'series', - 'token' - ], - - methods: { - randomToken: function() { - return Math.round((new Date().valueOf() * Math.random())) + ''; - }, - - save: function(fn) { - // Automatically create the tokens - this.token = this.randomToken(); - this.series = this.randomToken(); - this.__super__(fn); - } - }, - - getters: { - id: function() { + }); + + /** + * Model: LoginToken + * + * Used for session persistence. + */ + LoginToken = new Schema({ + email: { type: String, index: true }, + series: { type: String, index: true }, + token: { type: String, index: true } + }); + + LoginToken.method('randomToken', function() { + return Math.round((new Date().valueOf() * Math.random())) + ''; + }); + + LoginToken.pre('save', function(next) { + // Automatically create the tokens + this.token = this.randomToken(); + this.series = this.randomToken(); + next(); + }); + + LoginToken.virtual('id') + .get(function() { return this._id.toHexString(); - }, + }); - cookieValue: function() { + LoginToken.virtual('cookieValue') + .get(function() { return JSON.stringify({ email: this.email, token: this.token, series: this.series }); - } - } -}); + }); -exports.User = function(db) { - return db.model('User'); -}; + mongoose.model('Document', Document); + mongoose.model('User', User); + mongoose.model('LoginToken', LoginToken); -exports.Document = function(db) { - return db.model('Document'); -}; + fn(); +} -exports.LoginToken = function(db) { - return db.model('LoginToken'); -}; +exports.defineModels = defineModels; diff --git a/package.json b/package.json index 9adbde9..8d41414 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "express": "1.0.0", - "mongoose": "0.0.4", + "mongoose": "1.0.7", "connect": "0.5.1", "connect-mongodb": "0.1.1", "jade": "0.6.0", diff --git a/public/javascripts/application.js b/public/javascripts/application.js index a3cce45..01672a5 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -36,6 +36,28 @@ } }); + $('#logout').click(function(e) { + e.preventDefault(); + if (confirm('Are you sure you want to log out?')) { + var element = $(this), + form = $('
'); + form + .attr({ + method: 'POST', + action: '/sessions' + }) + .hide() + .append('') + .find('input') + .attr({ + 'name': '_method', + 'value': 'delete' + }) + .end() + .submit(); + } + }); + // Correct widths and heights based on window size function resize() { var height = $(window).height() - $('#header').height() - 1, @@ -87,7 +109,7 @@ $('#save-button').click(function() { var id = $('#document-list .selected').itemID(), - params = { d: { data: $('#editor').val(), id: id } }; + params = { d: { data: $('#editor').val(), id: id, title: $('#document-list .selected').html() } }; $.put('/documents/' + id + '.json', params, function(data) { // Saved, will return JSON }); diff --git a/test/app.test.js b/test/app.test.js index 4107004..aaa4c8e 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -1,3 +1,7 @@ +/** + * Run with expresso test/app.test.js + */ + var app = require('../app'), assert = require('assert'), zombie = require('zombie'), @@ -11,7 +15,9 @@ testHelper.models = [app.User]; testHelper.setup(function() { // Fixtures var user = new app.User({'email' : 'alex@example.com', 'password' : 'test' }); - user.save(testHelper.run(exports)); + user.save(function() { + testHelper.run(exports) + }); }); testHelper.tests = { diff --git a/test/helper.js b/test/helper.js index 61164ba..a6655ab 100644 --- a/test/helper.js +++ b/test/helper.js @@ -8,7 +8,7 @@ function prepare(models, next) { var modelCount = models.length; models.forEach(function(model) { modelCount--; - model.find().all(function(records) { + model.find({}, function(err, records) { var count = records.length; records.forEach(function(result) { result.remove(); diff --git a/views/layout.jade b/views/layout.jade index 4539563..bb58f2c 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -16,7 +16,7 @@ html a(href='/') #{nameAndVersion(appName, version)} - if (typeof currentUser !== 'undefined') li.right - a.destroy(href='/sessions') Log Out + a#logout(href='/sessions') Log Out !{flashMessages} != body script(type='text/javascript', src='/javascripts/application.js')