diff --git a/src/api.js b/src/api.js index 0513438..0bdeb44 100644 --- a/src/api.js +++ b/src/api.js @@ -144,9 +144,6 @@ Api.fn.session = { } return this.users.findWhere({'email': p_email}).then(function (user) { - - console.log(user); - if (!user) { throw Api.err.INVALID_CREDENTIAL; @@ -177,15 +174,17 @@ Api.fn.session = { throw Api.err.ALREADY_AUTHENTICATED; } - var token = this.tokens.get(p_token); - if (!token) - { - throw Api.err.INVALID_CREDENTIAL; - } + return this.tokens.get(p_token).then(function (token) { + if (!token) + { + throw Api.err.INVALID_CREDENTIAL; + } - session.set('token_id', token.id); - session.set('user_id', token.user_id); - return true; + session.set('token_id', token.get('id')); + session.set('user_id', token.get('user_id')); + return true; + + }); }, 'getUser': deprecated(function (session) { @@ -195,7 +194,9 @@ Api.fn.session = { return null; } - return _.pick(this.users.get(user_id), 'id', 'email'); + return this.users.get(user_id).then(function (user) { + return _.pick(user.properties, 'id', 'email'); + }); }), 'getUserId': function (session) { @@ -296,13 +297,8 @@ Api.fn.user = { return users.update(user); }).then( - function () { - return true; - }, - function () { - // @todo Find a better error. - return Api.err.INVALID_PARAMS; - } + true, + Q.reject(Api.err.INVALID_PARAMS) ); }, }; @@ -320,7 +316,7 @@ Api.fn.token = { // @todo Token permission. - this.tokens.generate(user_id).then(function (token) { + return this.tokens.generate(user_id).then(function (token) { return token.get('id'); }); }, @@ -328,13 +324,14 @@ Api.fn.token = { 'delete': function (session, req) { var p_token = req.params.token; - if (!this.tokens.get(p_token)) - { - throw Api.err.INVALID_PARAMS; - } + var tokens = this.tokens; + return tokens.get(p_token).then(function (token) { + if (!token) + { + throw Api.err.INVALID_PARAMS; + } - return this.tokens.remove(p_token).then(function () { - return true; + return tokens.remove(p_token).then(true); }); }, }; @@ -398,9 +395,7 @@ Api.fn.server = { // @todo Disconnect the server. - return this.servers.remove(p_id).then(function () { - return true; - }); + return this.servers.remove(p_id).then(true); }, 'connect': function () { diff --git a/src/collection.js b/src/collection.js index 777daaa..ae1c6e9 100644 --- a/src/collection.js +++ b/src/collection.js @@ -4,18 +4,18 @@ var Q = require('q'); ////////////////////////////////////////////////////////////////////// // @todo Add events. -function Collection(items) +function Collection(models) { // Parent constructor. Collection.super_.call(this); - this.items = []; + this.models = []; this.next_id = 0; - if (items) + if (models) { - this.add(items); + this.add(models); } } require('util').inherits(Collection, require('events').EventEmitter); @@ -23,56 +23,69 @@ require('util').inherits(Collection, require('events').EventEmitter); Collection.prototype.model = require('./model'); /** - * Adds new items to this collection. + * Adds new models to this collection. */ -Collection.prototype.add = function (items) { +Collection.prototype.add = function (models) { var array = true; - if (!_.isArray(items)) + if (!_.isArray(models)) { - items = [items]; + models = [models]; array = false; } - _.each(items, function (item, i) { - if ( !(item instanceof this.model) ) + _.each(models, function (model, i) { + if ( !(model instanceof this.model) ) { - item = new (this.model)(item); - items[i] = item; + model = new this.model(model); + models[i] = model; } - var error = item.validate(); + var error = model.validate(); if (undefined !== error) { // @todo Better system inspired by Backbone.js. throw error; } - var id = item.get('id'); + var id = model.get('id'); if (undefined === id) { id = this.next_id++; - item.set('id', id); + model.set('id', id); } - // Existing items are ignored. - if (this.items[id]) + // Existing models are ignored. + if (this.models[id]) { - return Q.reject('cannot add existing items!'); + return Q.reject('cannot add existing models!'); } - this.items[id] = item.properties; + this.models[id] = model.properties; }, this); /* jshint newcap: false */ - return Q(array ? items : items[0]); + return Q(array ? models : models[0]); +}; + +Collection.prototype.get = function (id) { + /* jshint newcap:false */ + + var model = this.models[id]; + + if (!model) + { + return Q(null); + } + + return Q(new this.model(model)); }; /** * */ Collection.prototype.exists = function (id) { - return (undefined !== this.items[id]); + return (undefined !== this.models[id]); }; /** @@ -81,11 +94,13 @@ Collection.prototype.exists = function (id) { Collection.prototype.findWhere = function (properties) { /* jshint newcap: false */ - return Q(_.findWhere(this.items, properties)); + var model = _.findWhere(this.models, properties); + + return Q(model ? new this.model(model) : null); }; /** - * Removes items from this collection. + * Removes models from this collection. */ Collection.prototype.remove = function (ids) { if (!_.isArray(ids)) @@ -94,8 +109,8 @@ Collection.prototype.remove = function (ids) { } _.each(ids, function (id) { - delete this.items[id]; - }); + delete this.models[id]; + }, this); // @todo Maybe return a more meaningful value. /* jshint newcap: false */ @@ -103,24 +118,25 @@ Collection.prototype.remove = function (ids) { }; /** - * Updates existing items. + * Updates existing models. */ -Collection.prototype.update = function (items) { +Collection.prototype.update = function (models) { var array = true; - if (!_.isArray(items)) + if (!_.isArray(models)) { - items = [items]; + models = [models]; array = false; } - _.each(items, function (properties, i) { + // @todo Rewrite. + _.each(models, function (properties, i) { if (properties instanceof this.model) { properties = properties.properties; } // @todo - // var error = item.validate(); + // var error = model.validate(); // if (undefined !== error) // { // // @todo Better system inspired by Backbone.js. @@ -129,31 +145,31 @@ Collection.prototype.update = function (items) { var id = properties.id; - var item = this.items[id]; + var model = this.models[id]; - // Missing items are ignored. - if (!item) + // Missing models are ignored. + if (!model) { - return Q.reject('missing item!'); + return Q.reject('missing model!'); } - item.set(properties); + _.extend(model.properties, model); - items[i] = item; + models[i] = model; }); /* jshint newcap: false */ - return Q(array ? items : items[0]); + return Q(array ? models : models[0]); }; /** * Smartly updates the collection. * - * - Adds new items. - * - Updates existing items. - * - Removes missing items. + * - Adds new models. + * - Updates existing models. + * - Removes missing models. */ -Collection.prototype.set = function (/*items*/) { +Collection.prototype.set = function (/*models*/) { throw 'not implemented'; }; diff --git a/src/main.js b/src/main.js index 2bae68d..3a5265d 100644 --- a/src/main.js +++ b/src/main.js @@ -63,7 +63,15 @@ function json_api_call(session, message) 'id': req.id, }); }, - format_error + function (error) { + if (error instanceof Error) + { + console.error(error); + return format_error(Api.err.SERVER_ERROR); + } + + return format_error(error); + } ); } @@ -149,4 +157,4 @@ require('net').createServer(function (socket) { socket.once('close', function () { session.close(); }); -}).listen(__dirname +'/../socket'); // @todo Should be configurable. +}).listen(8081); // @todo Should be configurable. diff --git a/src/model.js b/src/model.js index 582b126..6a2f45c 100644 --- a/src/model.js +++ b/src/model.js @@ -7,7 +7,7 @@ function Model(properties) // Parent constructor. Model.super_.call(this); - this.properties = this['default']; + this.properties = _.extend({}, this['default']); if (properties) { diff --git a/src/xo.js b/src/xo.js index ba13fd2..09520d2 100644 --- a/src/xo.js +++ b/src/xo.js @@ -35,12 +35,12 @@ var check = function () { // user.permission). var Token = Model.extend({ // Validates model attributes. - 'validate': function (attr) { - check(attr.id).len(10); - check(attr.user_id).isInt(); + // 'validate': function (attr) { + // check(attr.id).len(10); + // check(attr.user_id).isInt(); - return check.pop(); - }, + // return check.pop(); + // }, }, { 'generate': function (user_id) { return Q.ninvoke(crypto, 'randomBytes', 32).then(function (buf) { @@ -57,26 +57,21 @@ var User = Model.extend({ 'permission': 'none', }, - 'initialize': function () - { - this.on('change:password', function (model, password) { - this.unset('password', {'silent': true}); - - var user = this; - hashy.hash(password).then(function (hash) { - user.set('pw_hash', hash); - }).done(); - }); - }, - // Validates model attributes. - 'validate': function (attr) { - check(attr.id).isInt(); - check(attr.email).isEmail(); - check(attr.pw_hash).len(40); - check(attr.permission).isIn('none', 'read', 'write', 'admin'); + // 'validate': function (attr) { + // check(attr.id).isInt(); + // check(attr.email).isEmail(); + // check(attr.pw_hash).len(40); + // check(attr.permission).isIn('none', 'read', 'write', 'admin'); - return check.pop(); + // return check.pop(); + // }, + + 'setPassword': function (password) { + var self = this; + return hashy.hash(password).then(function (hash) { + self.set('pw_hash', hash); + }); }, // Checks and updates the hash if necessary. @@ -92,8 +87,10 @@ var User = Model.extend({ if (hashy.needsRehash(hash)) { - user.set('password', password); + return user.setPassword(password).then(true); } + + return true; }); }, @@ -122,8 +119,10 @@ var Tokens = Collection.extend({ 'model': Token, 'generate': function (user_id) { - return this.model.generate(user_id).then(function (token) { - return this.add(token); + var self = this; + + return Token.generate(user_id).then(function (token) { + return self.add(token); }); } }); @@ -143,6 +142,10 @@ function Xo() this.servers = new Servers(); this.tokens = new Tokens(); this.users = new Users(); + this.users.add({ + 'email': 'bob@gmail.com', + 'pw_hash': '$2a$10$PsSOXflmnNMEOd0I5ohJQ.cLty0R29koYydD0FBKO9Rb7.jvCelZq', + }).done(); // This events are used to automatically close connections if the // associated credentials are invalidated. @@ -157,7 +160,7 @@ function Xo() }); }); } -require('util').inherits(Collection, require('events').EventEmitter); +require('util').inherits(Xo, require('events').EventEmitter); module.exports = function () { return new Xo();