Properly modeling users ( #5 ), moving over to mongodb.
This commit is contained in:
parent
3dad352bba
commit
3004e5e326
9 changed files with 197 additions and 89 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,3 +13,5 @@ node_modules
|
|||
preload.sql
|
||||
|
||||
dev.db
|
||||
|
||||
tmp/
|
||||
|
|
148
app.js
148
app.js
|
@ -8,8 +8,14 @@ var multer = require('multer');
|
|||
var fs = require('fs');
|
||||
var markdown = require( "markdown" ).markdown;
|
||||
|
||||
|
||||
// Get connected to our database
|
||||
var MongoClient = require('mongodb').MongoClient
|
||||
|
||||
Post = require('./post.js');
|
||||
User = require('./user.js');
|
||||
var helper = require('./helper.js');
|
||||
var db = require('./db.js');
|
||||
var database = require('./db.js');
|
||||
var genPosts = require('./genPosts.js');
|
||||
var genStatic = require('./genStatic.js');
|
||||
var genPhotos = require('./genPhotos.js');
|
||||
|
@ -33,8 +39,11 @@ app.use(flash());
|
|||
app.use(require('morgan')('dev'));
|
||||
app.use(require('cookie-parser')());
|
||||
app.use(require('body-parser').urlencoded({ extended: true }));
|
||||
app.use(require('express-session')({ secret: 'amdasdfasdfamd',
|
||||
resave: true, saveUninitialized: true }));
|
||||
app.use(require('express-session')({
|
||||
secret: 'amdasdfasdfamd',
|
||||
resave: true,
|
||||
saveUninitialized: true })
|
||||
);
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
var upload = multer({ dest: config.uploadDir });
|
||||
|
@ -42,7 +51,8 @@ var upload = multer({ dest: config.uploadDir });
|
|||
// Setup authentication via twitter.
|
||||
passport.use(new Strategy(
|
||||
function(username, password, done) {
|
||||
db.getUser(username, password, function(user) {
|
||||
User.verify(username, password, function(err, user) {
|
||||
console.log("Verifying user: " + username);
|
||||
if (!user) { return done(null, false); }
|
||||
return done(null, user);
|
||||
});
|
||||
|
@ -50,11 +60,13 @@ passport.use(new Strategy(
|
|||
));
|
||||
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user.id);
|
||||
console.log("Serializing user: "+user.username);
|
||||
done(null, user.username);
|
||||
});
|
||||
|
||||
passport.deserializeUser(function(id, done) {
|
||||
db.getUserById(id, function (user) {
|
||||
passport.deserializeUser(function(username, done) {
|
||||
console.log("Deserializing user: "+username);
|
||||
User.getByUsername(username, function (err, user) {
|
||||
if (!user) { return done(false); }
|
||||
done(null, user);
|
||||
});
|
||||
|
@ -63,8 +75,8 @@ passport.deserializeUser(function(id, done) {
|
|||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
// Require logins for all admin pages.
|
||||
app.all('/admin/*', require('connect-ensure-login').ensureLoggedIn());
|
||||
// Require logins for all admin pages other pages have to be handled separately.
|
||||
app.all('/admin*', require('connect-ensure-login').ensureLoggedIn());
|
||||
|
||||
// User management routing
|
||||
app.get('/login', function(req, res) {
|
||||
|
@ -78,7 +90,8 @@ app.post('/login',
|
|||
app.get('/logout', function(req, res) {
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Post management routing
|
||||
app.get('/admin/post/list/:start?',
|
||||
|
@ -89,7 +102,7 @@ app.get('/admin/post/list/:start?',
|
|||
} else {
|
||||
var start = 0;
|
||||
}
|
||||
db.listPosts(count, start, function(posts){
|
||||
database.listPosts(count, start, function(posts){
|
||||
for (post in posts) {
|
||||
var date = new Date(posts[post].postDate);
|
||||
posts[post].dateString = date.getFullYear() + '-' +
|
||||
|
@ -98,12 +111,13 @@ app.get('/admin/post/list/:start?',
|
|||
}
|
||||
res.render('admin-post-list', {posts, user: req.user});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.get('/admin/post/view/:id?',
|
||||
function (req, res, next) {
|
||||
if (req.params.id) {
|
||||
db.getPostById(req.params.id, function(row) {
|
||||
database.getPostById(req.params.id, function(row) {
|
||||
res.render('admin-post-view', {
|
||||
successNotice: req.flash('successNotice'),
|
||||
failureNotice: req.flash('failureNotice'),
|
||||
|
@ -122,10 +136,11 @@ app.get('/admin/post/view/:id?',
|
|||
|
||||
app.get('/admin/post/new',
|
||||
function(req, res, next) {
|
||||
db.listCategories(function(rows){
|
||||
database.listCategories(function(rows){
|
||||
res.render('admin-post-new', { categories: rows, user: req.user });
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.post('/admin/post/new',
|
||||
function(req, res, next) {
|
||||
|
@ -146,36 +161,37 @@ app.post('/admin/post/new',
|
|||
var categoryName = req.body.category;
|
||||
var tags = helper.parseTags(req.body.tags);
|
||||
|
||||
db.createPost(title, slug, markdown, postDate, updatedDate, createDate, function(err, row) {
|
||||
db.setPostCategory(row.id, categoryName, function(category) {
|
||||
db.tagPost(row.id, tags);
|
||||
database.createPost(title, slug, markdown, postDate, updatedDate, createDate, function(err, row) {
|
||||
database.setPostCategory(row.id, categoryName, function(category) {
|
||||
database.tagPost(row.id, tags);
|
||||
req.flash('successNotice', 'Post created.');
|
||||
res.redirect('/admin/post/edit/'+row.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.get('/admin/post/edit/:id',
|
||||
function(req, res, next) {
|
||||
var id = req.params.id;
|
||||
async.parallel({
|
||||
categories: function(callback) {
|
||||
db.listCategories(function(categories) {
|
||||
database.listCategories(function(categories) {
|
||||
callback(null, categories);
|
||||
})
|
||||
},
|
||||
postCategory: function(callback) {
|
||||
db.getPostCategory(id, function(category) {
|
||||
database.getPostCategory(id, function(category) {
|
||||
callback(null, category);
|
||||
})
|
||||
},
|
||||
postTags: function(callback) {
|
||||
db.getPostTagsAsString(id, function(tagString) {
|
||||
database.getPostTagsAsString(id, function(tagString) {
|
||||
callback(null, tagString);
|
||||
})
|
||||
},
|
||||
post: function(callback) {
|
||||
db.getPostById(id, function(post) {
|
||||
database.getPostById(id, function(post) {
|
||||
callback(null, post);
|
||||
})
|
||||
}
|
||||
|
@ -197,7 +213,8 @@ app.get('/admin/post/edit/:id',
|
|||
user: req.user
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.post('/admin/post/edit/:id',
|
||||
function(req, res, next) {
|
||||
|
@ -209,14 +226,15 @@ app.post('/admin/post/edit/:id',
|
|||
var categoryName = req.body.category;
|
||||
var tags = helper.parseTags(req.body.tags);
|
||||
console.log('Post '+id+' update request received');
|
||||
db.tagPost(id, tags);
|
||||
db.updatePost(id, title, slug, markdown, postDate, function(err, row) {
|
||||
db.setPostCategory(id, categoryName, function(err) {
|
||||
database.tagPost(id, tags);
|
||||
database.updatePost(id, title, slug, markdown, postDate, function(err, row) {
|
||||
database.setPostCategory(id, categoryName, function(err) {
|
||||
req.flash('successNotice', 'Post updated.');
|
||||
res.redirect('/admin/post/edit/'+id);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.get('/admin/post/regenerate/:id?',
|
||||
function(req, res, next) {
|
||||
|
@ -237,7 +255,8 @@ app.get('/admin/post/regenerate/:id?',
|
|||
res.redirect('/admin/post/list');
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Photo management Routing
|
||||
app.get('/admin/photo/list/:start?',
|
||||
|
@ -248,7 +267,7 @@ app.get('/admin/photo/list/:start?',
|
|||
} else {
|
||||
var start = 0;
|
||||
}
|
||||
db.listPhotos(count, start, function(photos){
|
||||
database.listPhotos(count, start, function(photos){
|
||||
for (photo in photos) {
|
||||
var date = new Date(photos[photo].photoDate);
|
||||
photos[photo].dateString = date.getFullYear() + '-' +
|
||||
|
@ -257,12 +276,13 @@ app.get('/admin/photo/list/:start?',
|
|||
}
|
||||
res.render('admin-photo-list', {photos, user: req.user});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.get('/admin/photo/view/:id?',
|
||||
function (req, res, next) {
|
||||
if (req.params.id) {
|
||||
db.getPhotoById(req.params.id, function(row) {
|
||||
database.getPhotoById(req.params.id, function(row) {
|
||||
res.render('admin-photo-view', {
|
||||
successNotice: req.flash('successNotice'),
|
||||
failureNotice: req.flash('failureNotice'),
|
||||
|
@ -281,7 +301,8 @@ app.get('/admin/photo/view/:id?',
|
|||
app.get('/admin/photo/new',
|
||||
function(req, res, next) {
|
||||
res.render('admin-photo-new', { user: req.user });
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
app.post('/admin/photo/new',
|
||||
upload.single('photo'),
|
||||
|
@ -318,26 +339,27 @@ app.post('/admin/photo/new',
|
|||
var categoryName = req.body.category;
|
||||
var path = req.file.path;
|
||||
|
||||
db.createPhoto(title, slug, markdown, extension, mimetype, photoDate, updatedDate, createDate, path, function(err, row) {
|
||||
database.createPhoto(title, slug, markdown, extension, mimetype, photoDate, updatedDate, createDate, path, function(err, row) {
|
||||
if (err) console.log(err);
|
||||
console.log(row);
|
||||
db.tagPhoto(row.id, tags);
|
||||
database.tagPhoto(row.id, tags);
|
||||
req.flash('successNotice', 'Photo created.');
|
||||
res.redirect('/admin/photo/view/'+row.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.get('/admin/photo/edit/:id',
|
||||
function(req, res, next) {
|
||||
var id = req.params.id;
|
||||
async.parallel({
|
||||
photoTags: function(callback) {
|
||||
db.getPhotoTagsAsString(id, function(tagString) {
|
||||
database.getPhotoTagsAsString(id, function(tagString) {
|
||||
callback(null, tagString);
|
||||
})
|
||||
},
|
||||
photo: function(callback) {
|
||||
db.getPhotoById(id, function(photo) {
|
||||
database.getPhotoById(id, function(photo) {
|
||||
callback(null, photo);
|
||||
})
|
||||
}
|
||||
|
@ -353,7 +375,8 @@ app.get('/admin/photo/edit/:id',
|
|||
user: req.user
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.post('/admin/photo/edit/:id',
|
||||
function(req, res, next) {
|
||||
|
@ -364,65 +387,76 @@ app.post('/admin/photo/edit/:id',
|
|||
var photoDate = helper.dateToEpoch(new Date(req.body.photoDate));
|
||||
var tags = helper.parseTags(req.body.tags);
|
||||
console.log('Post '+id+' update request received');
|
||||
db.tagPhoto(id, tags);
|
||||
db.updatePhoto(id, title, slug, markdown, photoDate, function(err, row) {
|
||||
database.tagPhoto(id, tags);
|
||||
database.updatePhoto(id, title, slug, markdown, photoDate, function(err, row) {
|
||||
req.flash('successNotice', 'Photo updated.');
|
||||
res.redirect('/admin/photo/view/'+id);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
app.get('/admin/photo/regenerate/:id',
|
||||
function(req, res, next) {
|
||||
console.log('Generating resized images for: '+req.params.id);
|
||||
genStatic.generateStatic(function(err){ if (err) console.log(err) });
|
||||
genPhotos.generatePhotoSizesById(req.params.id, function(err) {
|
||||
if (!err) {
|
||||
req.flash('successNotice', 'Resized images created.');
|
||||
genPhotos.generatePhotoPage(req.params.id, function(err) {
|
||||
if (!err) {
|
||||
req.flash("successNotice", "Photo regenerated");
|
||||
res.redirect('/admin/photo/view/'+req.params.id);
|
||||
}
|
||||
else {
|
||||
req.flash('failureNotice', 'Photo was unable to be resized.');
|
||||
res.redirect('/admin/photo/view/'+req.params.id);
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
req.flash('failureNotice', 'Photo was unable to be resized.');
|
||||
res.redirect('/admin/photo/view/'+req.params.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Admin dashboard page.
|
||||
app.get('/admin',
|
||||
function(req, res, next) {
|
||||
async.parallel({
|
||||
categories: function(callback) {
|
||||
db.countCategories(function(count) {
|
||||
database.countCategories(function(count) {
|
||||
callback(null, count);
|
||||
});
|
||||
},
|
||||
posts: function(callback) {
|
||||
db.countPosts(function(count) {
|
||||
database.countPosts(function(count) {
|
||||
callback(null, count);
|
||||
});
|
||||
},
|
||||
galleries: function(callback) {
|
||||
db.countGalleries(function(count) {
|
||||
database.countGalleries(function(count) {
|
||||
callback(null, count);
|
||||
});
|
||||
},
|
||||
photos: function(callback) {
|
||||
db.countPhotos(function(count) {
|
||||
database.countPhotos(function(count) {
|
||||
callback(null, count);
|
||||
});
|
||||
},
|
||||
tags: function(callback) {
|
||||
db.countTags(function(count) {
|
||||
database.countTags(function(count) {
|
||||
callback(null, count);
|
||||
});
|
||||
},
|
||||
regenerateDate: function(callback) {
|
||||
db.getLastRegenerateDate(function(date) {
|
||||
database.getLastRegenerateDate(function(date) {
|
||||
var dateString = helper.epochToDateString(date.date).slice(0,-12);
|
||||
callback(null, dateString);
|
||||
});
|
||||
},
|
||||
uploadDate: function(callback) {
|
||||
db.getLastUploadDate(function(date) {
|
||||
database.getLastUploadDate(function(date) {
|
||||
var dateString = helper.epochToDateString(date.date).slice(0,-12);
|
||||
callback(null, dateString);
|
||||
});
|
||||
|
@ -442,7 +476,8 @@ app.get('/admin',
|
|||
user: req.user});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Routes for previewing sent versions of the built items.
|
||||
app.get('/blog/*',
|
||||
|
@ -585,11 +620,11 @@ app.get('/'+config.uploadDir+'/*',
|
|||
}
|
||||
);
|
||||
|
||||
|
||||
// Have to have some sort of home page.
|
||||
app.get('/', function(req, res, next) {
|
||||
res.render('admin-index', { title: 'AMDavidson.com', user: req.user });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use(function(req, res, next) {
|
||||
|
@ -628,6 +663,11 @@ app.use(function(err, req, res, next) {
|
|||
app.set('port', process.env.PORT || 3000)
|
||||
var server = require('http').createServer(app)
|
||||
|
||||
MongoClient.connect('mongodb://localhost/crunch', function(err, database) {
|
||||
if (!err) {
|
||||
global.db = database;
|
||||
server.listen(app.get('port'), function() {
|
||||
console.log("Server listening on port " + app.get('port'));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
28
db.js
28
db.js
|
@ -6,28 +6,6 @@ var helper = require('./helper.js');
|
|||
|
||||
var db = new sqlite.Database(config.dbPath);
|
||||
|
||||
// Function to get a user record by the username and password
|
||||
// Returns SQL row for that user
|
||||
exports.getUser = function(username, password, cb) {
|
||||
db.get('SELECT salt FROM users WHERE username = ?', username, function(err, row) {
|
||||
var hash = helper.hashPassword(password, row.salt);
|
||||
db.get('SELECT username, id, displayName, createDate \
|
||||
FROM users WHERE username = ? AND password = ?',
|
||||
username, hash, function(err, row) {
|
||||
cb(row);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Function to get a user record by id, does not validate user password
|
||||
// Returns SQL row for that user
|
||||
exports.getUserById = function(id, cb) {
|
||||
db.get('SELECT username, id, displayName, createDate \
|
||||
FROM users WHERE id = ?', id, function(err, row) {
|
||||
cb(row);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to get the latest regenerate date
|
||||
// Returns a epoch time in seconds
|
||||
exports.getLastRegenerateDate = function(cb) {
|
||||
|
@ -107,7 +85,8 @@ exports.listCategories = function(cb) {
|
|||
// Function to get a list of posts of a certain count and starting at an offset
|
||||
// Returns a list of post objects
|
||||
exports.listPosts = function(count, start, cb) {
|
||||
db.all('SELECT * FROM posts ORDER BY postDate DESC, title ASC \
|
||||
db.all('SELECT * FROM posts WHERE deleted = 0 \
|
||||
ORDER BY postDate DESC, title ASC \
|
||||
LIMIT '+count+' OFFSET '+start+';',
|
||||
function(err, posts) {
|
||||
console.log(err);
|
||||
|
@ -118,7 +97,8 @@ exports.listPosts = function(count, start, cb) {
|
|||
// Function to get a list of photos of a certain count and starting at an offset
|
||||
// Returns a list of photo objects
|
||||
exports.listPhotos = function(count, start, cb) {
|
||||
db.all('SELECT * FROM photos ORDER BY photoDate DESC, title ASC \
|
||||
db.all('SELECT * FROM photos WHERE deleted = 0 \
|
||||
ORDER BY photoDate DESC, title ASC \
|
||||
LIMIT '+count+' OFFSET '+start+';',
|
||||
function(err, photos) {
|
||||
console.log(err);
|
||||
|
|
BIN
dump/crunch/users.bson
Normal file
BIN
dump/crunch/users.bson
Normal file
Binary file not shown.
1
dump/crunch/users.metadata.json
Normal file
1
dump/crunch/users.metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"crunch.users"}]}
|
|
@ -13,7 +13,9 @@
|
|||
"express-session": "^1.12.0",
|
||||
"gm": "^1.21.1",
|
||||
"jade": "^1.11.0",
|
||||
"lodash": "^4.0.0",
|
||||
"markdown": "^0.5.0",
|
||||
"mongodb": "^2.1.4",
|
||||
"morgan": "^1.6.1",
|
||||
"multer": "^1.1.0",
|
||||
"ncp": "^2.0.0",
|
||||
|
|
10
schemas.js
Normal file
10
schemas.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
schemas = {
|
||||
user: {
|
||||
username: null,
|
||||
password: null,
|
||||
hash: null,
|
||||
email: null
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = schemas
|
72
user.js
Normal file
72
user.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
var crypto = require('crypto');
|
||||
var schemas = require("./schemas.js");
|
||||
var _ = require("lodash");
|
||||
|
||||
var User = function(data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
User.prototype.data = {}
|
||||
|
||||
User.prototype.get = function (name) {
|
||||
return this.data[name];
|
||||
}
|
||||
|
||||
User.prototype.set = function (name, value) {
|
||||
this.data[name] = value;
|
||||
}
|
||||
|
||||
User.prototype.sanitize = function (data) {
|
||||
data = data || {};
|
||||
schema = schemas.user;
|
||||
return _.pick(_.defaults(data, schema), _.keys(schema));
|
||||
}
|
||||
|
||||
User.prototype.save = function (callback) {
|
||||
var self = this;
|
||||
this.data = this.sanitize(this.data);
|
||||
db.collection("users").update({username:username},this.data, callback(err, count));
|
||||
}
|
||||
|
||||
// Function to look up a user document by the users username
|
||||
User.getByUsername = function (username, callback) {
|
||||
console.log("Getting user document for: "+username);
|
||||
db.collection("users").findOne({username:username}, function(err, doc) {
|
||||
callback(null, new User(doc));
|
||||
});
|
||||
}
|
||||
|
||||
// Function to verify authentication via a username and password.
|
||||
// Requires internal hashing function.
|
||||
User.verify = function (username, password, callback) {
|
||||
console.log("Verifying user: "+username);
|
||||
db.collection("users").findOne({username: username}, function(err, doc) {
|
||||
if (err || !doc) {
|
||||
console.log("Username "+username+" does not exist");
|
||||
console.log(err);
|
||||
callback(err, null);
|
||||
}
|
||||
else {
|
||||
console.log("Username "+username+" exists");
|
||||
console.log(username + "'s salt is " + doc.salt);
|
||||
var hash = hashPassword(password, doc.salt);
|
||||
console.log(username+"'s hashed password is "+hash);
|
||||
db.collection("users").findOne({username: username, password: hash},
|
||||
function(err, doc) {
|
||||
callback(err, doc);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Helper functions, internal to model
|
||||
|
||||
hashPassword = function(password, salt) {
|
||||
var hash = crypto.createHash('sha256');
|
||||
hash.update(password);
|
||||
hash.update(salt);
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
|
||||
module.exports = User;
|
|
@ -1,5 +1,6 @@
|
|||
div(class="col-sm-2 sidebar")
|
||||
ul(class="nav nav-sidebar")
|
||||
li: a #{user.get("username")}
|
||||
li: a(href="/admin/") Dashboard
|
||||
|
||||
ul(class="nav nav-sidebar")
|
||||
|
|
Loading…
Reference in a new issue