Properly modeling users ( #5 ), moving over to mongodb.

This commit is contained in:
Andrew Davidson 2016-01-16 16:40:58 -05:00
parent 3dad352bba
commit 3004e5e326
9 changed files with 197 additions and 89 deletions

2
.gitignore vendored
View file

@ -13,3 +13,5 @@ node_modules
preload.sql
dev.db
tmp/

162
app.js
View file

@ -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'),
@ -275,13 +295,14 @@ app.get('/admin/photo/view/:id?',
else {
res.redirect('/admin/photo/list');
}
}
}
);
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.');
res.redirect('/admin/photo/view/'+req.params.id);
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/*',
@ -470,7 +505,7 @@ app.get('/blog/*',
}
});
}
}
}
);
app.get('/photos/*',
@ -526,7 +561,7 @@ app.get('/galleries/*',
}
});
}
}
}
);
app.get('/static/*',
@ -554,7 +589,7 @@ app.get('/static/*',
}
});
}
}
}
);
app.get('/'+config.uploadDir+'/*',
@ -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)
server.listen(app.get('port'), function() {
console.log("Server listening on port " + app.get('port'));
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
View file

@ -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

Binary file not shown.

View file

@ -0,0 +1 @@
{"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"crunch.users"}]}

View file

@ -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
View file

@ -0,0 +1,10 @@
schemas = {
user: {
username: null,
password: null,
hash: null,
email: null
}
}
module.exports = schemas

72
user.js Normal file
View 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;

View file

@ -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")