WIP persistence layer and stage account api

This commit is contained in:
Zachary Carter 2012-11-13 09:44:11 -08:00
Родитель add29d59f5
Коммит 41657124dc
11 изменённых файлов: 223 добавлений и 41 удалений

14
bin/api
Просмотреть файл

@ -24,11 +24,21 @@ server.on('bound', function(host, port) {
console.log("running on http://" + host + ":" + port);
});
// TODO seperate config
var dbConfig = {
hosts: [ 'localhost:8091' ],
username: 'admin',
password: null,
bucket: 'default'
};
// now load up api handlers
apiLoader(server, function(err) {
if (err) fatal(err);
// Start the server
server.start();
db.connect(dbConfig, function() {
// Start the server
server.start();
});
});
process.on('SIGTERM', function() {

Просмотреть файл

@ -1,39 +1,79 @@
GombotClient = (function() {
var xhr = typeof jQuery !== 'undefined' ? jQuery.ajax : require('xhrequest');
;(function() {
function request(args, cb) {
var url = args.scheme ? args.scheme : 'http';
var method = args.method.toUpperCase();
url += "://" + args.host;
if (args.port) url += ":" + args.port;
url += args.path;
GombotClient = function(host, port) {
this.host = host;
this.port = port;
};
console.log('sending xhr');
xhr(url, {
method: method,
success: function (data, res, status) {
try {
var body = JSON.parse(data);
body.session_context = {};
cb(null, body);
} catch (e) {
cb('Invalid JSON response: '+e);
}
},
error: function (data, res, status) {
cb('Error: '+data+'\nStatus: '+status);
var xhr = typeof jQuery !== 'undefined' ? jQuery.ajax : require('xhrequest');
if (typeof GombotCrypto === 'undefined') {
var GombotCrypto = require('./crypto.js');
}
function request(args, cb) {
var url = args.scheme ? args.scheme : 'http';
var method = args.method.toUpperCase();
url += '://' + args.host;
if (args.port) url += ':' + args.port;
url += args.path;
var req = {
url: url,
method: method,
data: args.data,
headers: {},
success: function(data, res, status) {
try {
var body = JSON.parse(data);
} catch (e) {
return cb('Invalid JSON response: ' + e);
}
});
}
return {
// get "session context" from the server
context: function(args, cb) {
args.method = 'get';
args.path = '/v1/context';
request(args, cb);
body.session_context = {};
cb(null, body);
},
error: function(data, res, status) {
cb('Error: ' + data + '\nStatus: ' + status);
}
};
if (method == 'PUT' || method == 'POST') {
req.headers['Content-Type'] = 'application/json';
}
xhr(url, req);
}
GombotClient.prototype = {
// get "session context" from the server
context: function(args, cb) {
if (typeof args === 'function') {
cb = args;
args = {};
}
args.host = args.host || this.host;
args.port = args.port || this.port;
args.method = 'get';
args.path = '/v1/context';
request(args, cb);
},
account: function(args, cb) {
args.host = args.host || this.host;
args.port = args.port || this.port;
args.method = 'put';
args.path = '/v1/account';
// compute the authKey
var keys = GombotCrypto.derive({
email: args.email,
password: args.password
}, function(err, r) {
args.data = JSON.stringify({email: args.email, pass: r.authKey});
// send request with authKey as the password
request(args, cb);
});
}
};
})();
if (typeof module != 'undefined' && module.exports) {

32
lib/api/v1/account.js Normal file
Просмотреть файл

@ -0,0 +1,32 @@
var crypto = require('crypto'),
Hapi = require('hapi'),
db = require('../../db');
var B = Hapi.Types.Boolean,
S = Hapi.Types.String;
module.exports = {
method: 'PUT',
handler: handler,
config: {
auth: false,
description: 'Stage a new account',
schema: {
email: B(),
pass: S()
},
response: {
success: B()
}
}
};
function handler(request) {
console.log('$$$$$$$$$$');
db.stageAccount(request.payload, function(err) {
if (err) request.reply(Hapi.Error.internal("error staging account"));
request.reply({
success: true
});
});
}

Просмотреть файл

@ -5,7 +5,7 @@ const API_BASE_PATH = path.join(__dirname, 'api');
function addRouteFromFile(hapiServer, apiPath) {
// hack off ext
var route = apiPath.substr(0,apiPath.length - 3);
var route = apiPath.substr(0, apiPath.length - 3);
var route = path.relative(API_BASE_PATH, route);
var impl = require(apiPath);
impl.path = '/' + route;
@ -23,7 +23,7 @@ module.exports = function(hapiServer, cb) {
try {
addRouteFromFile(hapiServer, apiPath);
} catch(e) {
} catch (e) {
walker.end();
cb('error while loading API handler ("' + apiPath + '") ' +
e.toString());

3
lib/db.js Normal file
Просмотреть файл

@ -0,0 +1,3 @@
// TODO select driver based on config
module.exports = require('./db/json');

29
lib/db/couchbase.js Normal file
Просмотреть файл

@ -0,0 +1,29 @@
var couchbase = require('couchbase');
var db;
module.exports = {
connect: function(options, cb) {
console.error('options', options);
couchbase.connect(options, function(err, bucket) {
console.error('connection fail!!!', err);
if (err) cb(err);
db = bucket;
cb(null);
});
return this;
},
stageAccount: function(data, cb) {
var account = {
pass: data.pass,
email: data.email,
staged: true
};
db.set(data.email, account, function (err, meta) {
console.log('meta', meta);
if (err) cb(err);
else cb(null, meta);
});
return this;
}
};

23
lib/db/json.js Normal file
Просмотреть файл

@ -0,0 +1,23 @@
var db;
module.exports = {
connect: function(options, cb) {
setTimeout(function() {
if (db) return;
db = {};
cb(null);
}, 0);
},
stageAccount: function(data, cb) {
var account = {
pass: data.pass,
email: data.email,
staged: true
};
setTimeout(function() {
db[data.email] = account;
cb(null);
}, 0);
return this;
}
};

Просмотреть файл

@ -6,6 +6,7 @@
"dependencies": {
"hapi": "git://github.com/lloyd/hapi#b12270",
"walkdir": "0.0.5",
"couchbase": "0.0.4",
"express": "3.0.2",
"nunjucks": "0.1.5"
},

42
test/api.v1.account.js Normal file
Просмотреть файл

@ -0,0 +1,42 @@
const
should = require('should'),
runner = require('./lib/runner.js'),
Client = require('../client/client.js');
var servers;
var client;
describe('the servers', function() {
it('should start up', function(done) {
runner(function(err, r) {
should.not.exist(err);
should.exist(r);
servers = r;
client = new Client(servers.host, servers.port);
done();
});
});
});
describe('/api/v1/account', function() {
it('staging should return success', function(done) {
client.account({
email: 'foo',
password: 'bar'
}, function(err, r) {
should.not.exist(err);
should.exist(r);
(r.success).should.be.true;
done();
});
});
});
describe('the servers', function() {
it('should stop', function(done) {
servers.stop(function(err) {
should.not.exist(err);
done();
});
});
});

Просмотреть файл

@ -1,9 +1,10 @@
const
should = require('should'),
runner = require('./lib/runner.js'),
client = require('../client/client.js');
Client = require('../client/client.js');
var servers;
var client;
describe('the servers', function() {
it('should start up', function(done) {
@ -11,6 +12,7 @@ describe('the servers', function() {
should.not.exist(err);
should.exist(r);
servers = r;
client = new Client(servers.host, servers.port);
done();
});
});
@ -18,10 +20,7 @@ describe('the servers', function() {
describe("/api/v1/context", function() {
it ("should return an object with two keys", function(done) {
client.context({
host: servers.host,
port: servers.port
}, function(err, r) {
client.context(function(err, r) {
should.not.exist(err);
should.exist(r);
should.exist(r.session_context);

Просмотреть файл

@ -19,7 +19,7 @@ module.exports = function(cb) {
if (!s.started) {
// we parse app output to determine when the process has really
data.toString().split("\n").forEach(function(line) {
var m = /^bound to ([a-zA-Z0-9_.]+):([0-9]+)$/.exec(line);
var m = /running on http:\/\/([a-zA-Z0-9_.]+):([0-9]+)$/.exec(line);
if (m) {
s.started = true;
s.host = m[1];
@ -28,6 +28,9 @@ module.exports = function(cb) {
}
});
}
if (process.env.LOG_TO_CONSOLE) {
process.stdout.write(data);
}
});
s.server.stderr.on('data', function (data) {