* Module dependencies.
*/
-var express = require('express')
+var errorHandler = require('./lib/middleware/errorHandler')
+ , express = require('express')
, http = require('http')
, port = require('./config').port
, redisstore = require('connect-redis')(express)
rolling: true,
store: sessionstore
}));
+app.use(app.router);
+app.use(errorHandler);
// Routes
app.get('/', site.home);
--- /dev/null
+/**
+ * Basic error handling middleware.
+ */
+
+module.exports = function(err, req, res, next) {
+ if (Array.isArray(err)) {
+ err.forEach(function(err) {
+ console.error(err.message);
+ });
+ }
+ else {
+ console.error(err.message);
+ }
+ res.send(500);
+};
var amatch = require('./match')
, clients = require('./redis-clients')
- , collectStats = require('./stats')
, config = require('../config')
, fifolength = config.songsinarun * config.gameswithnorepeats
, primus
, rooms = {} // The Object that contains all the room instances
, songsdb = clients.songs
, sparks
+ , updateStats = require('./stats')
, usersdb = clients.users
, utils = require('./utils')
, isString = utils.isString
if (usersData[nickname].registered) {
stats.userscore = usersData[nickname].points;
stats.guesstime = usersData[nickname].guesstime;
- collectStats(nickname, stats);
+ updateStats(nickname, stats);
}
};
// Collect podium stats
if (podium[0] && podium[0].registered) {
- collectStats(podium[0].nickname, {firstplace:true});
+ updateStats(podium[0].nickname, {firstplace:true});
}
if (podium[1] && podium[1].registered) {
- collectStats(podium[1].nickname, {secondplace:true});
+ updateStats(podium[1].nickname, {secondplace:true});
}
if (podium[2] && podium[2].registered) {
- collectStats(podium[2].nickname, {thirdplace:true});
+ updateStats(podium[2].nickname, {thirdplace:true});
}
resetPoints(false);
spark.send('artistmatched');
primus.room(roomname).send('updateusers', usersData);
if (usersData[spark.nickname].registered) {
- var stats = {points:1,userscore:usersData[spark.nickname].points};
- collectStats(spark.nickname, stats);
+ var stats = {points:1, userscore:usersData[spark.nickname].points};
+ updateStats(spark.nickname, stats);
}
}
else if (amatch(title, guess)) {
spark.send('titlematched');
primus.room(roomname).send('updateusers', usersData);
if (usersData[spark.nickname].registered) {
- var stats = {points:1,userscore:usersData[spark.nickname].points};
- collectStats(spark.nickname, stats);
+ var stats = {points:1, userscore:usersData[spark.nickname].points};
+ updateStats(spark.nickname, stats);
}
}
else {
if (usersData[who]) {
// Inform the bad player that he/she is being ignored
var recipient = sparks[who];
- recipient.send('chatmsg', executor+' is ignoring you.', 'binb', who);
+ recipient.send('chatmsg', executor + ' is ignoring you.', 'binb', who);
return callback(true, who);
}
callback(false);
// Kick a user
this.kick = function(who, why, executor, callback) {
- usersdb.hget('user:'+executor, 'role', function (err, role) {
+ usersdb.hget(['user:' + executor, 'role'], function(err, role) {
+ if (err) {
+ console.error(err.message);
+ return callback(true);
+ }
if (role > 0) { // Check role
if (usersData[who]) {
if (why) {
- why = ' ('+why+')';
+ why = ' (' + why + ')';
}
- var notice = 'you have been kicked by '+executor+why+'.';
+ var notice = 'you have been kicked by ' + executor + why + '.';
var recipient = sparks[who];
recipient.send('chatmsg', notice, 'binb', who);
recipient.end();
// Extract a random track from the database and send the load event
var sendLoadTrack = function() {
var index = randInt(trackscount);
- songsdb.zrange(roomname, index, index, function(err, res) {
+ songsdb.zrange([roomname, index, index], function(err, res) {
+ if (err) {
+ console.error(err.message);
+ process.exit(1);
+ }
var id = res[0];
// Check if extracted track is in the list of already played tracks
if (~playedtracks.indexOf(id)) {
}
playedtracks.push(id);
var args = [
- 'song:'+id
+ 'song:' + id
, 'artistName'
, 'trackName'
, 'previewUrl'
, 'artworkUrl60'
, 'trackViewUrl'
];
- songsdb.hmget(args, function(e, replies) {
+ songsdb.hmget(args, function(err, replies) {
+ if (err) {
+ console.error(err.message);
+ process.exit(1);
+ }
artistName = replies[0];
artist = artistName.toLowerCase();
trackName = replies[1];
// A user is submitting a name
this.setNickName = function(spark, nickname) {
- var feedback = null;
+ var feedback;
if (nickname === 'binb') {
- feedback = '<span class="label label-important">That name is reserved.</span>';
+ feedback = '<span class="label label-important">That name ' +
+ 'is reserved.</span>';
}
else if (!isUsername(nickname)) {
- feedback = '<span class="label label-important">Name must contain only ';
- feedback += 'alphanumeric characters.</span>';
+ feedback = '<span class="label label-important">Name must ' +
+ 'contain only alphanumeric characters.</span>';
}
else if (sparks[nickname]) {
- feedback = '<span class="label label-important">Name already taken.</span>';
+ feedback = '<span class="label label-important">Name ' +
+ 'already taken.</span>';
}
if (feedback) {
return spark.send('invalidnickname', feedback);
}
- // Check if requested nickname belong to a registered user
- var key = 'user:'+nickname;
- usersdb.exists(key, function(err, resp) {
- if (resp === 1) {
- feedback = '<span class="label label-important">That name belongs ';
- feedback += 'to a registered user.</span>';
+ // Check if requested nickname belongs to a registered user
+ var key = 'user:' + nickname;
+ usersdb.exists([key], function(err, exists) {
+ if (err) {
+ console.error(err.message);
+ feedback = '<span class="label label-important">Could not ' +
+ 'check name availability.</span>';
+ return spark.send('invalidnickname', feedback);
+ }
+ if (exists) {
+ feedback = '<span class="label label-important">That name ' +
+ 'belongs to a registered user.</span>';
return spark.send('invalidnickname', feedback);
}
spark.nickname = nickname;
// Start the room
this.start = function() {
- songsdb.zcard(roomname, function(err, card) {
+ songsdb.zcard([roomname], function(err, card) {
+ if (err) {
+ console.error(err.message);
+ process.exit(1);
+ }
trackscount = card;
sendLoadTrack();
});
this.unignore = function(who, executor) {
if (usersData[who]) {
// Inform the bad player that he/she is no longer ignored
- var notice = executor+' has stopped ignoring you.';
+ var notice = executor + ' has stopped ignoring you.';
var recipient = sparks[who];
recipient.send('chatmsg', notice, 'binb', who);
}
var db = require('./redis-clients').users;
/**
- * Expose a function to collect user statistics.
+ * Update user statistics.
*/
-module.exports = function(username, stats) {
- var key = 'user:'+username;
+var updateStats = function(key, multi, username, stats) {
if (stats.points) {
// Update total points
- db.hincrby(key, 'totpoints', stats.points);
+ multi.hincrby(key, 'totpoints', stats.points);
// Update the score of the member in the sorted set
- db.zincrby('users', stats.points, username);
- }
- if (stats.userscore) {
- // Set personal best
- db.hget(key, 'bestscore', function(err, res) {
- if (res < stats.userscore) {
- db.hset(key, 'bestscore', stats.userscore);
- }
- });
+ multi.zincrby('users', stats.points, username);
}
if (stats.gold) {
// Update the number of golds
- db.hincrby(key, 'golds', 1);
+ multi.hincrby(key, 'golds', 1);
}
if (stats.silver) {
- db.hincrby(key, 'silvers', 1);
+ multi.hincrby(key, 'silvers', 1);
}
if (stats.bronze) {
- db.hincrby(key, 'bronzes', 1);
- }
- if (stats.guesstime) {
- // Update the number of guessed tracks
- db.hincrby(key, 'guessed', 1);
- // Update total guess time
- db.hincrby(key, 'totguesstime', stats.guesstime);
- // Set best answer time
- db.hget(key, 'bestguesstime', function(err, res) {
- if (stats.guesstime < res) {
- db.hset(key, 'bestguesstime', stats.guesstime);
- }
- });
- // Set worst answer time
- db.hget(key, 'worstguesstime', function(err, res) {
- if (stats.guesstime > res) {
- db.hset(key, 'worstguesstime', stats.guesstime);
- }
- });
+ multi.hincrby(key, 'bronzes', 1);
}
if (stats.firstplace) {
// Update the number of first places
- db.hincrby(key, 'victories', 1);
+ multi.hincrby(key, 'victories', 1);
}
if (stats.secondplace) {
- db.hincrby(key, 'secondplaces', 1);
+ multi.hincrby(key, 'secondplaces', 1);
}
if (stats.thirdplace) {
- db.hincrby(key, 'thirdplaces', 1);
+ multi.hincrby(key, 'thirdplaces', 1);
+ }
+ multi.exec(function(err, replies) {
+ if (err) {
+ err.forEach(function(err) {
+ console.error(err.message);
+ });
+ }
+ });
+};
+
+/**
+ * Expose a function to update user statistics.
+ */
+
+module.exports = function(username, stats) {
+ var key = 'user:' + username
+ , multi = db.multi();
+ if (stats.guesstime) {
+ var args = [
+ key
+ , 'bestscore'
+ , 'bestguesstime'
+ , 'worstguesstime'
+ ];
+ db.hmget(args, function(err, replies) {
+ if (err) {
+ return console.error(err.message);
+ }
+ if (stats.userscore > replies[0]) {
+ // Set personal best
+ multi.hset(key, 'bestscore', stats.userscore);
+ }
+ // Update the number of guessed tracks
+ multi.hincrby(key, 'guessed', 1);
+ // Update total guess time
+ multi.hincrby(key, 'totguesstime', stats.guesstime);
+ // Set best answer time
+ if (stats.guesstime < replies[1]) {
+ multi.hset(key, 'bestguesstime', stats.guesstime);
+ }
+ // Set worst answer time
+ if (stats.guesstime > replies[2]) {
+ multi.hset(key, 'worstguesstime', stats.guesstime);
+ }
+ updateStats(key, multi, username, stats);
+ });
+ return;
+ }
+ if (stats.userscore) {
+ db.hget([key, 'bestscore'], function(err, bestscore) {
+ if (err) {
+ return console.error(err.message);
+ }
+ if (stats.userscore > bestscore) {
+ multi.hset(key, 'bestscore', stats.userscore);
+ }
+ updateStats(key, multi, username, stats);
+ });
+ return;
}
+ updateStats(key, multi, username, stats);
};
"start": "node app.js"
},
"subdomain": "binb",
- "version": "0.3.6-21"
+ "version": "0.4.0"
}
var subTask = function(genre) {
return function(callback) {
var index = randInt(rooms[genre].tracksCount());
- db.zrange(genre, index, index, function(err, res) {
- db.hget('song:'+res[0], 'artworkUrl100', callback);
+ db.zrange([genre, index, index], function(err, res) {
+ if (err) {
+ return callback(err);
+ }
+ db.hget(['song:' + res[0], 'artworkUrl100'], callback);
});
};
};
* Extract at random in each room, some album covers and return the result as a JSON.
*/
-exports.artworks = function(req, res) {
+exports.artworks = function(req, res, next) {
var tasks = {};
config.rooms.forEach(function(room) {
tasks[room] = function(callback) {
};
});
async.parallel(tasks, function(err, results) {
- res.json(results);
+ if (err) {
+ return next(err);
+ }
+ res.send(results);
});
};
* Show two lists of users, one ordered by points and one by best guess time (limit set to 30).
*/
-exports.leaderboards = function(req, res) {
- db.zrevrange('users', 0, 29, 'withscores', function(err, pointsresults) {
- db.sort(utils.sortParams(0), function(e, timesresults) {
+exports.leaderboards = function(req, res, next) {
+ db.zrevrange(['users', 0, 29, 'withscores'], function(err, pointsresults) {
+ if (err) {
+ return next(err);
+ }
+ db.sort(utils.sortParams(0), function(err, timesresults) {
+ if (err) {
+ return next(err);
+ }
var leaderboards = utils.buildLeaderboards(pointsresults, timesresults);
res.locals.slogan = utils.randomSlogan();
res.render('leaderboards', leaderboards);
* Get 30 users from the ranking, starting at index `begin`.
*/
-exports.sliceLeaderboard = function(req, res) {
+exports.sliceLeaderboard = function(req, res, next) {
var begin = parseInt(req.query.begin, 10)
, by = req.query.by;
if (isNaN(begin) || begin > 180 || (by !== 'points' && by !== 'times')) {
}
var end = begin + 29;
if (by === 'points') {
- db.zrevrange('users', begin, end, 'withscores', function(err, results) {
- res.json(results);
+ db.zrevrange(['users', begin, end, 'withscores'], function(err, results) {
+ if (err) {
+ return next(err);
+ }
+ res.send(results);
});
return;
}
db.sort(utils.sortParams(begin), function(err, results) {
- res.json(results);
+ if (err) {
+ return next(err);
+ }
+ res.send(results);
});
};
};
exports.checkOldPasswd = function(req, res, next) {
- var key = 'user:'+req.session.user;
- db.hmget(key, 'salt', 'password', function(err, data) {
- var hash = crypto.createHash('sha256').update(data[0]+req.body.oldpassword).digest('hex');
- if (hash !== data[1]) {
+ var key = 'user:' + req.session.user;
+ db.hmget([key, 'salt', 'password'], function(err, data) {
+ if (err) {
+ return next(err);
+ }
+ var digest
+ , hash = crypto.createHash('sha256');
+ hash.update(data[0] + req.body.oldpassword);
+ digest = hash.digest('hex');
+ if (digest !== data[1]) {
req.session.errors = {oldpassword: 'is incorrect'};
return res.redirect(req.url);
}
});
};
-exports.changePasswd = function(req, res) {
- var followup = ~safeurls.indexOf(req.query.followup) ? req.query.followup : '/'
+exports.changePasswd = function(req, res, next) {
+ var digest
+ , followup = ~safeurls.indexOf(req.query.followup) ? req.query.followup : '/'
, user = req.session.user
- , key = 'user:'+user
- , salt = crypto.randomBytes(6).toString('base64')
- , password = crypto.createHash('sha256').update(salt+req.body.newpassword).digest('hex');
- db.hmset(key, 'salt', salt, 'password', password, function(err, data) {
+ , key = 'user:' + user
+ , hash = crypto.createHash('sha256')
+ , salt = crypto.randomBytes(6).toString('base64');
+ hash.update(salt + req.body.newpassword);
+ digest = hash.digest('hex');
+ db.hmset([key, 'salt', salt, 'password', digest], function(err, data) {
+ if (err) {
+ return next(err);
+ }
// Regenerate the session
req.session.regenerate(function() {
req.session.cookie.maxAge = 604800000; // One week
};
exports.checkUser = function(req, res, next) {
- var key = 'user:'+req.body.username;
- db.exists(key, function(err, data) {
- if (data === 1) {
+ var key = 'user:' + req.body.username;
+ db.exists([key], function(err, exists) {
+ if (err) {
+ return next(err);
+ }
+ if (exists) {
// User exists, proceed with authentication
return next();
}
});
};
-exports.authenticate = function(req, res) {
- var key = 'user:'+req.body.username;
- db.hmget(key, 'salt', 'password', function(err, data) {
- var hash = crypto.createHash('sha256').update(data[0]+req.body.password).digest('hex');
- if (hash === data[1]) {
+exports.authenticate = function(req, res, next) {
+ var key = 'user:' + req.body.username;
+ db.hmget([key, 'salt', 'password'], function(err, data) {
+ if (err) {
+ return next(err);
+ }
+ var digest
+ , hash = crypto.createHash('sha256');
+ hash.update(data[0] + req.body.password);
+ digest = hash.digest('hex');
+ if (digest === data[1]) {
var followup = ~safeurls.indexOf(req.query.followup) ? req.query.followup : '/';
// Authentication succeeded, regenerate the session
req.session.regenerate(function() {
};
exports.userExists = function(req, res, next) {
- var key = 'user:'+req.body.username;
- db.exists(key, function(err, data) {
- if (data === 1) {
+ var key = 'user:' + req.body.username;
+ db.exists([key], function(err, exists) {
+ if (err) {
+ return next(err);
+ }
+ if (exists) {
// User already exists
req.session.errors = {alert: 'A user with that name already exists.'};
return res.redirect(req.url);
};
exports.emailExists = function(req, res, next) {
- var key = 'email:'+req.body.email;
- db.exists(key, function(err, data) {
- if (data === 1) {
+ var key = 'email:' + req.body.email;
+ db.exists([key], function(err, exists) {
+ if (err) {
+ return next(err);
+ }
+ if (exists) {
// Email already exists
req.session.errors = {alert: 'A user with that email already exists.'};
return res.redirect(req.url);
});
};
-exports.createAccount = function(req, res) {
- var userkey = 'user:'+req.body.username
- , mailkey = 'email:'+req.body.email
+exports.createAccount = function(req, res, next) {
+ var digest
+ , hash = crypto.createHash('sha256')
+ , mailkey = 'email:' + req.body.email
, salt = crypto.randomBytes(6).toString('base64')
- , hash = crypto.createHash('sha256').update(salt+req.body.password).digest('hex')
- , date = new Date().toISOString()
- , user = new User(req.body.username, req.body.email, salt, hash, date);
+ , userkey = 'user:' + req.body.username;
+ hash.update(salt + req.body.password);
+ digest = hash.digest('hex');
+ var date = new Date().toISOString()
+ , user = new User(req.body.username, req.body.email, salt, digest, date);
- // Add new user in the db
- db.hmset(userkey, user);
- db.set(mailkey, userkey);
- db.zadd('users', 0, req.body.username);
- db.sadd('emails', req.body.email);
// Delete old fields values
delete req.session.oldvalues;
- res.render('login', {
- followup: req.query.followup || '/',
- slogan: utils.randomSlogan(),
- success: 'You successfully created your account. You are now ready to login.'
+
+ // Add new user in the db
+ var multi = db.multi();
+ multi.hmset(userkey, user);
+ multi.set(mailkey, userkey);
+ multi.zadd('users', 0, req.body.username);
+ multi.sadd('emails', req.body.email);
+ multi.exec(function(err, replies) {
+ if (err) {
+ return next(err);
+ }
+ res.render('login', {
+ followup: req.query.followup || '/',
+ slogan: utils.randomSlogan(),
+ success: 'You successfully created your account. You are now ready to login.'
+ });
});
};
next();
};
-exports.sendEmail = function(req, res) {
- var key = 'email:'+req.body.email;
- db.get(key, function(err, data) {
- if (data !== null) {
- // Email exists, generate a secure random token
+exports.sendEmail = function(req, res, next) {
+ var key = 'email:' + req.body.email;
+ db.get([key], function(err, data) {
+ if (err) {
+ return next(err);
+ }
+ if (data) {
delete req.session.captchacode;
+ delete req.session.oldvalues;
+ // Email exists, generate a secure random token
var token = crypto.randomBytes(48).toString('hex');
// Token expires after 4 hours
- db.setex('token:'+token, 14400, data, function(err, reply) {
+ db.setex(['token:' + token, 14400, data], function(err, reply) {
+ if (err) {
+ return next(err);
+ }
mailer.sendEmail(req.body.email, token, function(err, response) {
if (err) {
console.error(err.message);
}
});
+ res.render('recoverpasswd', {
+ followup: req.query.followup || '/',
+ slogan: utils.randomSlogan(),
+ success: true
+ });
});
- delete req.session.oldvalues;
- return res.render('recoverpasswd', {
- followup: req.query.followup || '/',
- slogan: utils.randomSlogan(),
- success: true
- });
+ return;
}
req.session.errors = {alert: 'The email address you specified could not be found'};
res.redirect(req.url);
* Reset user password.
*/
-exports.resetPasswd = function(req, res) {
+exports.resetPasswd = function(req, res, next) {
if (req.body.password === undefined) {
return res.send(400);
}
return res.redirect(req.url);
}
- var key = 'token:'+req.query.token;
- db.get(key, function(err, user) {
- if (user !== null) {
- // Delete the token
- db.del(key);
- // Update password
- var salt = crypto.randomBytes(6).toString('base64');
- var password = crypto.createHash('sha256').update(salt+req.body.password).digest('hex');
- db.hmset(user, 'salt', salt, 'password', password, function(err, data) {
+ var key = 'token:' + req.query.token;
+ db.get([key], function(err, user) {
+ if (err) {
+ return next(err);
+ }
+ if (user) {
+ db.del(key); // Delete the token
+ var digest
+ , hash = crypto.createHash('sha256')
+ , salt = crypto.randomBytes(6).toString('base64');
+ hash.update(salt + req.body.password);
+ digest = hash.digest('hex');
+ db.hmset([user, 'salt', salt, 'password', digest], function(err, data) {
+ if (err) {
+ return next(err);
+ }
res.render('login', {
followup: '/',
slogan: utils.randomSlogan(),
* Show user profile.
*/
-exports.profile = function(req, res) {
- var key = 'user:'+req.params.username;
- db.exists(key, function(err, data) {
- if (data === 1) {
- db.hgetall(key, function(e, obj) {
- obj.bestguesstime = (obj.bestguesstime/1000).toFixed(1);
- obj.joindate = utils.britishFormat(new Date(obj.joindate));
- if (obj.guessed !== '0') {
- obj.meanguesstime = ((obj.totguesstime/obj.guessed)/1000).toFixed(1);
+exports.profile = function(req, res, next) {
+ var key = 'user:' + req.params.username;
+ db.exists([key], function(err, exists) {
+ if (err) {
+ return next(err);
+ }
+ if (exists) {
+ db.hgetall([key], function(err, user) {
+ if (err) {
+ return next(err);
+ }
+ var joindate = new Date(user.joindate);
+ user.bestguesstime = (user.bestguesstime / 1000).toFixed(1);
+ user.joindate = utils.britishFormat(joindate);
+ if (user.guessed !== '0') {
+ user.meanguesstime = user.totguesstime / user.guessed;
+ user.meanguesstime = (user.meanguesstime / 1000).toFixed(1);
}
- obj.worstguesstime = (obj.worstguesstime/1000).toFixed(1);
- delete obj.email;
- delete obj.password;
- delete obj.salt;
- delete obj.totguesstime;
+ user.worstguesstime = (user.worstguesstime / 1000).toFixed(1);
+ delete user.email;
+ delete user.password;
+ delete user.salt;
+ delete user.totguesstime;
res.locals.slogan = utils.randomSlogan();
- res.render('user', obj);
+ res.render('user', user);
});
return;
}