/user.js Secret
Created
June 25, 2013 16:10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var EventEmitter = require('events').EventEmitter, | |
util = require('util'), | |
_ = require('underscore'), | |
crypto = require('crypto'); | |
var Setup = require('./setup'); | |
var mysqlPool = Setup.mysqlPool; | |
// These are added to account.prefs if they don't exist. This means that when the user gets updated, these will go with it. | |
// There hidden up here because we don't want to cause confusion. | |
var PREF_DEFAULTS = { | |
condenseTable: false, | |
stickyFooter: true, | |
pageRowLiimit: 30, | |
openSearch: false, | |
comicSans: false, | |
darkMode: false, | |
antipodean: false | |
}; | |
function User() { | |
EventEmitter.call(this); | |
var self = this; | |
this.loggedIn = false; | |
this.account = {}; | |
this.account.prefs = {}; | |
return this; | |
}; | |
util.inherits(User, EventEmitter); | |
var noop = function() { /* */ }; | |
User.prototype.fetch = function(agent_id) { | |
EventEmitter.call(this); | |
var self = this; | |
if(typeof agent_id === 'undefined') throw Error("User#fetch requires an agent id."); | |
mysqlPool.getConnection(function(err, db) { | |
db.query("SELECT * FROM _login_ WHERE agent_id = ?", [agent_id], function(err, rows) { | |
if(err) { | |
db.end(); | |
throw err; | |
} | |
var tmpAcc = {}; | |
if(rows.length > 0) { | |
tmpAcc = rows[0]; | |
tmpAcc.picture = self.accountPicture(rows[0].agent_name); | |
tmpAcc.prefs = _.defaults(JSON.parse(tmpAcc.prefs), PREF_DEFAULTS); | |
self.emit('fetched', tmpAcc); | |
}else{ | |
self.emit('fetched', false); | |
} | |
db.end(); | |
}); | |
}) | |
return this; | |
}; | |
/** | |
* This function takes over from autoLogin and manualLogin, requiring | |
* oAcc to be an object containing the relevant information. | |
*/ | |
User.prototype.login = function(oAcc, callback) { | |
EventEmitter.call(this); | |
var self = this; | |
callback = callback || noop; | |
if(typeof oAcc !== 'object') throw Error("User#login requires first parameter to be an object."); | |
// If the user is manually logging in we need to encode some shiz. | |
if(oAcc.manual) { | |
var md5sum = crypto.createHash('md5'); | |
md5sum.update(oAcc.password); | |
oAcc.password = md5sum.digest('hex'); // Override, we need it. | |
} | |
mysqlPool.getConnection(function(err, db) { | |
if(err) { | |
db.end(); | |
throw err; | |
} | |
db.query("SELECT agent_id FROM _login_ WHERE user = ? AND pass = ?", [oAcc.username, oAcc.password], function(err, rows) { | |
if(err) { | |
db.end(); | |
throw err; | |
} | |
if(rows.length > 0) { | |
// This may seem backwards, but we now call fetch which returns the data. | |
self.fetch(rows[0].agent_id); | |
self.once('fetched', function(account) { | |
if(account) { | |
self.loggedIn = true; | |
self.account = account; // Already formmatted. Boosh! | |
self.emit('logged in', account); | |
callback(err, account); | |
}else{ | |
self.emit('logged in', false); | |
callback(err, false); | |
} | |
self.removeAllListeners('fetched'); | |
db.end(); | |
}); | |
}else{ | |
self.emit('logged in', false); | |
callback(err, false); | |
} | |
db.end(); | |
}); | |
}); | |
return this; | |
}; | |
User.prototype.logout = function(callback) { | |
EventEmitter.call(this); | |
callback = callback || noop; | |
this.set('status', 0, true); | |
this.account = {}; | |
this.loggedIn = false; | |
this.emit('logged out'); | |
callback(); | |
return this; | |
}; | |
User.prototype.accountPicture = function(agent_name) { | |
this.account.picture = '/img/people/' + (agent_name || this.account.agent_name).toLowerCase().replace(/\s/, '-') + '.jpg'; | |
return this.account.picture; | |
}; | |
User.prototype.get = function(p) { | |
if(!this.loggedIn) throw Error("User#get requires the user to be logged in."); | |
if(this.account[p]) { | |
return this.account[p]; | |
}else{ | |
throw Error("User#get cannot return " + p + " - doesn't exist."); | |
} | |
}; | |
User.prototype.set = function(p, v) { | |
EventEmitter.call(this); | |
if(!this.loggedIn) throw Error("User#set requires the user to be logged in."); | |
var self = this; | |
if(typeof p === 'object') { | |
// Multi item | |
_.each(_.keys(p), function(item) { | |
self.account[item] = p[item]; | |
}); | |
}else{ | |
// Single item | |
if(typeof v === "undefined") throw Error("User#set must pass property and value."); | |
// Check that we can set this value | |
if(_.has(this.account, p)) { | |
this.account[p] = v; | |
}else{ | |
throw Error("User#set cannot set " + p + " - doesn't exist in _login_. Use User#pref"); | |
} | |
} | |
// Unlike Quote#set we can update the _login_ table straight away since these values won't change often | |
this.save(function(done) { | |
self.emit('pref saved', done); | |
}); | |
return this; | |
}; | |
/* | |
* Pref is funky. It's a two-pronged function. | |
* Pass it one value, it'll return it. | |
* Pass it two values and it sets it. | |
*/ | |
User.prototype.pref = function(prefName, prefValue, callback) { | |
EventEmitter.call(this); | |
if(!this.loggedIn) throw Error("User#pref requires the user to be logged in."); | |
callback = callback || noop; | |
// One value passed, return it. | |
if(typeof prefValue === 'undefined') { | |
if(this.account.prefs[prefName]) { | |
return this.account.prefs[prefName]; | |
}else{ | |
return false; // It doesn't exist. No point crying. | |
} | |
} | |
// Set the preference. We have to cheat here since User#set doesn't know about sub-objects. | |
this.account.prefs[prefName] = prefValue; | |
this.save(function() { | |
callback(); | |
}); | |
return this; | |
}; | |
User.prototype.delPref = function(prefName, callback) { | |
EventEmitter.call(this); | |
if(!this.loggedIn) throw Error("User#pref requires the user to be logged in."); | |
callback = callback || noop; | |
if(this.account.prefs[prefName]) { | |
delete this.account.prefs[prefName]; | |
this.save(function() { | |
callback(true); | |
}); | |
return true; | |
}else{ | |
callback(false); | |
return false; // It doesn't exist. No point crying. | |
} | |
return this; | |
}; | |
User.prototype.save = function(callback) { | |
EventEmitter.call(this); | |
if(!this.loggedIn) throw Error("User#save requires the user to be logged in."); | |
var self = this; | |
callback = callback || noop; | |
mysqlPool.getConnection(function(err, db) { | |
if(err) { | |
db.end(); | |
throw err; | |
} | |
var keyNames = [], keyValues = []; | |
var tableKeysString = ""; | |
var tmpAccountData = self.account; | |
_.each(_.keys(tmpAccountData), function(key, index) { | |
// For preferences, we need to re-encode the object. | |
if(key !== 'picture') { | |
if(key == "prefs") tmpAccountData[key] = JSON.stringify(tmpAccountData[key]); | |
tableKeysString += "`"+key+"` = ?, "; | |
keyValues.push(tmpAccountData[key]); | |
} | |
}); | |
tableKeysString = tableKeysString.substr(0, tableKeysString.length - 2); | |
db.query("UPDATE _login_ SET " + tableKeysString + " WHERE agent_id = " + self.account.agent_id, keyValues, function(err, rows) { | |
if(err) { | |
console.log(this.sql); | |
self.emit('saved', false); | |
callback(false); | |
db.end(); | |
throw err; | |
} | |
self.fetch(tmpAccountData.agent_id); | |
self.once('fetched', function(account) { | |
if(account) { | |
self.account = account; | |
self.emit('saved', true); | |
callback(true); | |
}else{ | |
self.emit('saved', false); | |
callback(false) | |
} | |
self.removeAllListeners('fetched'); | |
db.end(); | |
}) | |
}); | |
}); | |
return this; | |
}; | |
/** | |
* Removes all EventEmitter listeners. | |
*/ | |
User.prototype.deafen = function() { | |
this.removeAllListeners(); | |
}; | |
/** | |
* Alias for User#find but for active users only. | |
*/ | |
User.prototype.findActive = function(callback) { | |
EventEmitter.call(this); | |
if(typeof callback === "undefined") throw Error("User#findActive does not support EventEmitter yet. A callback is required."); | |
this.find({ | |
active: 1 | |
}, function(activeUsers) { | |
callback(activeUsers); | |
}); | |
return this; | |
}; | |
/** | |
* Alias for User#find but by a team. | |
*/ | |
User.prototype.findInTeam = function(team, callback) { | |
EventEmitter.call(this); | |
if(typeof team === "undefined") throw Error("User#findInTeam requires a team number."); | |
if(typeof callback === "undefined") throw Error("User#findInTeam does not support EventEmitter yet. A callback is required."); | |
this.find({ | |
team: team | |
}, function(usersInTeam) { | |
callback(usersInTeam); | |
}); | |
return this; | |
}; | |
/** | |
* Alias for User#find but for non-users (as in Website). | |
*/ | |
User.prototype.findNonUsers = function(callback) { | |
EventEmitter.call(this); | |
if(typeof callback === "undefined") throw Error("User#findNonUsers does not support EventEmitter yet. A callback is required."); | |
this.find({ | |
level: 0, | |
team: 7, | |
active: 1 | |
}, ['agent_id', 'agent_name'], function(nonUsers) { | |
callback(nonUsers); | |
}); | |
return this; | |
}; | |
/** | |
* Returns users from _login_, can be filtered by passing an object. | |
*/ | |
User.prototype.find = function(findBy, selectedFields, callback) { | |
EventEmitter.call(this); | |
var self = this, findByValues = [], findByClause = ""; | |
// findBy can only be an object, otherwise it's what selectedFields should be. | |
if(typeof findBy === 'object') { | |
findByValues = _.values(findBy); | |
findByClause = "WHERE "; | |
_.each(_.keys(findBy), function(value) { | |
findByClause += "`"+value+"` = ? AND "; | |
}); | |
findByClause = findByClause.substr(0, findByClause.length - 5); | |
if(typeof selectedFields === 'function') { | |
callback = selectedFields; | |
selectedFields = "*"; | |
} | |
}else if(typeof findBy === 'array') { | |
callback = selectedFields; | |
selectedFields = findBy.join(", "); | |
}else if(typeof findBy === 'string') { | |
callback = selectedFields; | |
selectedFields = findBy; | |
}else if(typeof findBy === 'function') { | |
callback = findBy; | |
selectedFields = "*"; | |
} | |
mysqlPool.getConnection(function(err, db) { | |
db.query("SELECT " + selectedFields + " FROM _login_ " + findByClause, findByValues, function(err, results) { | |
if(err) { | |
db.end(); | |
callback(false); | |
} | |
callback(results); | |
db.end(); | |
}); | |
}); | |
return this; | |
}; | |
/** | |
* Returns sources. Not really users, but it's handy to have them here. | |
*/ | |
User.prototype.sources = function(callback) { | |
EventEmitter.call(this); | |
var sources = []; | |
if(typeof callback === 'undefined') throw Error("User#sources must be passed a callback."); | |
mysqlPool.getConnection(function(err, db) { | |
var lookupSrc = db.query("SELECT * FROM _source_ WHERE src_active = 1"); | |
lookupSrc.on('error', function(err) { | |
if(err) { | |
db.end(); | |
throw err; | |
} | |
}).on('result', function(row) { | |
sources[row.src_id] = row; | |
}).on('end', function() { | |
callback(sources); | |
db.end(); | |
}); | |
}) | |
return this; | |
}; | |
exports = module.exports = User; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment