restart project with express framework

This commit is contained in:
Meutel 2014-05-22 19:08:24 +02:00
parent 055e24e68a
commit 8b9ceb7978
16 changed files with 5 additions and 1140 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
*.rej *.rej
*~ *~
*.*.swp *.*.swp
node_modules

View File

@ -1,66 +0,0 @@
#!/usr/bin/env nodejs
/**
* Bouquins bootstrap.
* TODO license
*
* Load configuration
* Check configuration
* Launch http server
* Initialise router
*/
APP_NAME='Bouquins';
DEFAULT_CONF='./config/config.json';
var bouquins = require('../lib/bouquins');
console.log('Bootstraping ' + APP_NAME);
// TODO argument conf file
var configfile = DEFAULT_CONF;
bouquins.loadconfig(configfile, function(err, config) {
if (err) {
console.error('Fatal error, cannot load config: ' + err);
console.log(err.stack);
process.exit(1);
}
// global config
GLOBAL.config = config;
// global logger
GLOBAL.logger = bouquins.initLogger();
logger.debug(config);
// database
bouquins.initDB(function(err) {
if (err) {
logger.error('Fatal error, cannot load database: ' + err);
logger.error(err.stack);
process.exit(1);
}
});
start();
});
/**
* Initialise application
*/
function start(){
logger.info('Starting '+APP_NAME);
var starReqListener=function(req, res) {
// defaut request listener
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(APP_NAME + ' is starting');
}
// launch http server, wait config and router
var server = require('http').createServer(starReqListener)
.listen(config.httpPort);
logger.info('Listening on '+config.httpPort);
// TODO check config: files/dir do exist, database exist, can read ...
// init router
var router = bouquins.makeRouter();
server.on('request', function(req, resp) {
router.request(req, resp);
}).removeListener('request', starReqListener);
}

View File

@ -1,13 +1,5 @@
{ {
"httpPort":8080, "httpPort":8080,
"debugLevel":"debug", "debugLevel":"debug",
"endpoints": {
"library":"endpoint/library.js",
"book":"endpoint/book.js",
"author":"endpoint/author.js",
"serie":"endpoint/serie.js",
"tag":"endpoint/tag.js"
},
"urlPrefix": "http://127.0.0.1:8080",
"dbfile": "/home/meutel/metadata.db" "dbfile": "/home/meutel/metadata.db"
} }

View File

@ -1,201 +0,0 @@
/**
* Action.
*/
var EventEmitter = require('events').EventEmitter;
var util = require('util');
/**
* Constructor.
*/
function Action(name) {
logger.debug('new Action');
EventEmitter.call(this);
//
// ATTRIBUTES
//
this.name = name;
//
// INIT
//
};
// injerits EventEmitter
Action.prototype = Object.create(EventEmitter.prototype, {
//
// FUNCTIONS
//
/**
* @return action needs request body data.
*/
withReqData: {
value: function() {
return false;
},
enumerable: true,
configurable: true,
writable: true
},
/**
* Listen request data event.
*/
reqData: {
value: function(chunk) {
//TODO
},
enumerable: true,
configurable: true,
writable: true
},
/**
* Prepare action.
* @param callback (err, statusCode, headers)
*/
prepare: {
value: function(callback) {
logger.debug('prepare Action');
// TODO impl per action type
// default: OK, no headers
callback(null, 200, {});
},
enumerable: true,
configurable: true,
writable: true
},
doAction: {
value: function(){
logger.debug('doAction');
//TODO emit data end events
this.emit('end');
},
enumerable: true,
configurable: true,
writable: true
}
});
/**
* Action show single resource.
*/
function ShowAction() {
logger.debug('new ShowAction');
Action.call(this, 'show');
// Resource id.
this.resId = null;
// Resource.
this.res = null;
};
// inherits Action
ShowAction.prototype = Object.create(Action.prototype, {
/**
* Prepare show action.
* Headers:
* - Link: related resource.
*/
prepare: {
value: function(callback) {
var self = this;
if (this.resId) {
this.loadResource(this.resId, function(err, res) {
self.res = res;
logger.debug('resource loaded: ' + res);
//TODO err
var link = '';
var rels = self.getRelated(res, function(err, rels) {
//TODO err
logger.debug(rels);
rels.forEach(function (e) {
if (link.length > 0) link+=', ';
link += '<' + config.urlPrefix + e.path + '>; rel=' + e.type;
});
var headers = {};
if (link.length > 0) {
headers.Link = link;
}
callback(null, 200, headers);
}); // { type: 'author', path: '/author/id'}
});
} else {
//TODO error?
callback(null, '5OO', {});
}
}, enumerable: true, configurable: true, writable: true
},
/**
* Load resource.
* @param resource id
* @param callback <function(err, resource)>
*/
loadResource: {
value: function(resId, callback){
//implement
callback(new Error('Cannot load resource ' + resId));
}, enumerable: true, configurable: true, writable: true
},
/**
* Get related resources.
* @param res resource
* @param callback <function(err, rels)> related resources { type: 'author', path: '/author/id'}
*/
getRelated: {
value: function(res, callback){
callback(null, []);
}, enumerable: true, configurable: true, writable: true
},
doAction: {
value: function(){
logger.debug('doAction');
this.emit('data', this.res);
this.emit('end');
}, enumerable: true, configurable: true, writable: true
}
});
/**
* Action show list of resources.
*/
function ListAction() {
logger.debug('new ListAction');
Action.call(this, 'list');
};
// inherits Action
ListAction.prototype = Object.create(Action.prototype, {
prepare: {
value: function(callback) {
//TODO
callback(null, 200, {});
}, enumerable: true, configurable: true, writable: true
},
doAction: {
value: function() {
logger.debug('doAction');
var self = this;
// TODO parameters
this.loadResources(function(err, res) {
// for each loaded resource
self.emit('data', res);
}, function(err) {
self.emit('end');
});
}, enumerable: true, configurable: true, writable: true
},
loadResources: {
value: function(onload, onend) {
// nothing to load
onend(null);
}, enumerable: true, configurable: true, writable: true
}
});
module.exports = {
ShowAction: ShowAction,
ListAction: ListAction
};

View File

@ -1,44 +0,0 @@
/**
* TODO license
* Bouquins module.
*/
GLOBAL.PATH_RE=/\/([a-zA-Z0-9]+)(?:\/|$)([a-zA-Z0-9]+)?/;
var Config = require('./util/config'),
logger = require('./util/logger'),
Router = require('./router/router'),
sqlite3 = require('sqlite3').verbose(),
bouquins = exports;
var router = null;
/**
* Load config file.
*/
bouquins.loadconfig = function(configfile, callback) {
Config.loadconfig(configfile, callback);
};
/**
* Init logger.
*/
bouquins.initLogger = function() {
if (config.debugLevel) {
logger.debugLevel = config.debugLevel;
}
return logger;
};
/**
* Init database.
*/
bouquins.initDB = function(callback) {
logger.debug('Database: '+config.dbfile);
GLOBAL.db = new sqlite3.Database(config.dbfile, callback);
};
/**
* Make main router.
*/
bouquins.makeRouter = function() {
if (!router) {
router = new Router();
}
return router;
};

View File

@ -1,73 +0,0 @@
/**
* Endpoint author.
*/
var Endpoint = require('./endpoint.js');
function Author() {
Endpoint.call(this);
this.authorId = null;
}
Author.prototype = Object.create(Endpoint.prototype, {
bind: {
value: function(action, params, callback) {
switch (action.name) {
case 'show':
action.resId = this.authorId;
action.getRelated = function(res, relcback){
// books of this autor
// TODO authors writing same book
relcback(null, new Array({ type: 'books', path: '/books?author='+this.resId }));
};
action.loadResource = function(resId, loadcback) {
logger.debug('loading author ' + resId);
db.get('SELECT * FROM authors WHERE id = '+ resId, function(err, row) {
loadcback(err, row);
});
};
callback(null, action);
break;
case 'list':
//TODO related
action.loadResources = function(onload, onend) {
var query = 'SELECT * FROM authors ';
if (!this.perPage) this.perPage = 30;
//TODO sanitize
if (!this.page) this.page = 0;
query += ' LIMIT ? OFFSET ?';
db.each(query, this.perPage, this.page*this.perPage, function (err, row) {
onload(err, row);
}, function(err) {
//TODO err
if (err) logger.error(err);
onend();
});
};
callback(null, action);
break;
default:
callback(new Error('action not implemented'));
}
},
enumerable: true,
configurable: true,
writable: true
},
targetCollection : {
value: function(pathname) {
var match = PATH_RE.exec(pathname);
logger.debug('pathname ' + pathname + ' => ' + match);
if (match.length > 2 && match[2]) {
// TODO check integer
this.authorId = match[2];
return false;
}
return true;
},
enumerable: true,
configurable: true,
writable: true
}
});
exports = module.exports = new Author();

View File

@ -1,88 +0,0 @@
/**
* Endpoint book.
*/
var Endpoint = require('./endpoint.js');
function Book() {
Endpoint.call(this);
this.bookId = null;
}
Book.prototype = Object.create(Endpoint.prototype, {
bind: {
value: function(action, params, callback) {
switch (action.name) {
case 'show':
action.resId = this.bookId;
action.getRelated = function(res,relcback){
var rels = new Array();
var nbrels = 3; // number of requests for rel
// call callback when no more requests running
var endrels = function(err) {
nbrels--;
if (err) logger.error(err);
if (nbrels == 0) relcback(null, rels);
};
var raq = 'SELECT * FROM books_authors_link WHERE book = ?';
db.each(raq, res.id, function (err, row) {
if (!err && row.author)
rels.push({ type: 'author', path: '/author/'+row.author });
}, endrels);
var rsq = 'SELECT * FROM books_series_link WHERE book = ?';
db.each(rsq, res.id, function (err, row) {
if (!err && row.series)
rels.push({ type: 'serie', path: '/serie/'+row.series });
}, endrels);
var rtq = 'SELECT * FROM books_tags_link WHERE book = ?';
db.each(rtq, res.id, function (err, row) {
if (!err && row.tag)
rels.push({ type: 'tag', path: '/tag/'+row.tag });
}, endrels);
};
action.loadResource = function(resId, callback) {
logger.debug('loading book ' + resId);
db.get('SELECT * FROM books WHERE id = '+ resId, function(err, row) {
callback(err, row);
});
};
callback(null, action);
break;
case 'list':
//TODO related
action.loadResources = function(onload, onend) {
var query = 'SELECT * FROM books ';
//TODO factorize
if (!this.perPage) this.perPage = 30;
//TODO sanitize
if (!this.page) this.page = 0;
query += ' LIMIT ? OFFSET ?';
db.each(query, this.perPage, this.page*this.perPage, function (err, row) {
onload(err, row);
}, function(err) {
//TODO err
if (err) logger.error(err);
onend();
});
};
callback(null, action);
break;
default:
callback(new Error('action not implemented'));
}
}, enumerable: true, configurable: true, writable: true
},
targetCollection : {
value: function(pathname) {
var match = PATH_RE.exec(pathname);
logger.debug('pathname ' + pathname + ' => ' + match);
if (match.length > 2 && match[2]) {
// TODO check integer
this.bookId = match[2];
return false;
}
return true;
}, enumerable: true, configurable: true, writable: true
}
});
exports = module.exports = new Book();

View File

@ -1,56 +0,0 @@
/**
* Endpoint class.
*/
var Action = require('../action/action.js');
function Endpoint() {
// constructor
};
Endpoint.prototype = {
/**
* Build an action on resource endpoint.
* @param method <string> HTTP method
* @param url <url> target URL
* @param callback <function(err, action)> callback
*/
buildAction : function(method, url, callback) {
var col = this.targetCollection(url.pathname);
logger.debug('Building action ('+method+','+col+')');
var action;
if (col && method == 'POST') {
//TODO search
} else if (col && method == 'GET') {
action = new Action.ListAction();
action.page = url.query.page;
action.perPage = url.query.per_page;
} else if (!col && method == 'POST') {
//TODO edit
} else if (!col && method == 'GET') {
action = new Action.ShowAction();
}
if (action) {
this.bind(action, url.query, function(err) {
callback(err, action);
});
} else {
callback(new Error('no action'));
}
},
/**
* @return target is a collection of resources.
*/
targetCollection : function(pathname) {
// TODO
return false;
},
/**
* Bind action to endpoint resource.
*/
bind : function(action, params, callback) {
// implemented
callback(null, action);
}
};
exports = module.exports = Endpoint;

View File

@ -1,44 +0,0 @@
/**
* Endpoint library.
*/
var Endpoint = require('./endpoint.js');
function Library() {
Endpoint.call(this);
}
Library.prototype = Object.create(Endpoint.prototype, {
bind: {
value: function(action, params, callback) {
if (action.name == 'show') {
action.resId = 'library';
action.loadResource = function(resId, callback) {
//TODO load from db
callback(null, {
name: 'Bibliothèque Meutel'
});
};
action.getRelated = function(res, relcback){
relcback(null, new Array(
// authors list
{ type: 'authors', path: '/author/' },
// books list
{ type: 'books', path: '/books/' },
// tags list
{ type: 'tags', path: '/tags/' },
// series list
{ type: 'series', path: '/series/' },
// user favorites
{ type: 'favorites', path:'/favorites/'}
));
};
}
callback(null, action);
},
enumerable: true,
configurable: true,
writable: true
},
});
exports = module.exports = new Library();

View File

@ -1,85 +0,0 @@
/**
* Endpoint serie.
*/
var Endpoint = require('./endpoint.js');
function Serie() {
Endpoint.call(this);
this.serieId = null;
}
Serie.prototype = Object.create(Endpoint.prototype, {
bind: {
value: function(action, params, callback) {
switch (action.name) {
case 'show':
action.resId = this.serieId;
action.getRelated = function(res,relcback){
var rels = new Array();
var nbrels = 2; // number of requests for rel
// call callback when no more requests running
var endrels = function(err) {
nbrels--;
if (err) logger.error(err);
if (nbrels == 0) relcback(null, rels);
};
// author
var raq = 'SELECT DISTINCT author FROM books_series_link AS bsl, books_authors_link AS bal WHERE bsl.book = bal.book AND bsl.series = ?';
db.each(raq, res.id, function (err, row) {
if (!err && row.author)
rels.push({ type: 'author', path: '/author/'+row.author });
}, endrels);
// books
var rbq = 'SELECT * FROM books_series_link WHERE series = ?';
db.each(rbq, res.id, function (err, row) {
if (!err && row.book)
rels.push({ type: 'book', path: '/book/'+row.book });
}, endrels);
};
action.loadResource = function(resId, callback) {
logger.debug('loading serie ' + resId);
db.get('SELECT * FROM series WHERE id = '+ resId, function(err, row) {
callback(err, row);
});
};
callback(null, action);
break;
case 'list':
//TODO related
action.loadResources = function(onload, onend) {
var query = 'SELECT * FROM series ';
//TODO factorize
if (!this.perPage) this.perPage = 30;
//TODO sanitize
if (!this.page) this.page = 0;
query += ' LIMIT ? OFFSET ?';
db.each(query, this.perPage, this.page*this.perPage, function (err, row) {
onload(err, row);
}, function(err) {
//TODO err
if (err) logger.error(err);
onend();
});
};
callback(null, action);
break;
default:
callback(new Error('action not implemented'));
}
}, enumerable: true, configurable: true, writable: true
},
targetCollection : {
value: function(pathname) {
var match = PATH_RE.exec(pathname);
logger.debug('pathname ' + pathname + ' => ' + match);
if (match.length > 2 && match[2]) {
// TODO check integer
this.serieId = match[2];
return false;
}
return true;
}, enumerable: true, configurable: true, writable: true
}
});
exports = module.exports = new Serie();

View File

@ -1,61 +0,0 @@
/**
* Endpoint tag.
*/
var Endpoint = require('./endpoint.js');
function Tag() {
Endpoint.call(this);
this.tagId = null;
}
Tag.prototype = Object.create(Endpoint.prototype, {
bind: {
value: function(action, params, callback) {
switch (action.name) {
case 'show':
action.resId = this.tagId;
action.loadResource = function(resId, callback) {
logger.debug('loading tag ' + resId);
db.get('SELECT * FROM tags WHERE id = '+ resId, function(err, row) {
callback(err, row);
});
};
callback(null, action);
break;
case 'list':
action.loadResources = function(onload, onend) {
var query = 'SELECT * FROM tags ';
//TODO factorize
if (!this.perPage) this.perPage = 30;
//TODO sanitize
if (!this.page) this.page = 0;
query += ' LIMIT ? OFFSET ?';
db.each(query, this.perPage, this.page*this.perPage, function (err, row) {
onload(err, row);
}, function(err) {
//TODO err
if (err) logger.error(err);
onend();
});
};
callback(null, action);
break;
default:
callback(new Error('action not implemented'));
}
}, enumerable: true, configurable: true, writable: true
},
targetCollection : {
value: function(pathname) {
var match = PATH_RE.exec(pathname);
logger.debug('pathname ' + pathname + ' => ' + match);
if (match.length > 2 && match[2]) {
// TODO check integer
this.tagId = match[2];
return false;
}
return true;
}, enumerable: true, configurable: true, writable: true
}
});
exports = module.exports = new Tag();

View File

@ -1,205 +0,0 @@
/**
* TODO license
* Outputter.
*/
/**
* Outputter class.
* Abstract (not exported).
*/
function Outputter() {
//
// ATTRIBUTES
//
/**
* Output stream.
*/
this.out = null;
this.headers = {};
};
Outputter.prototype = {
//
// FUNCTIONS
//
/**
* Add header to request.
* Available before calling outputTo().
*/
addHeader: function (name, value) {},
/**
* Init before outputing.
*/
init: function () {},
/**
* Listen action 'data' event.
* Receive resource instance to output.
*/
output: function (resource) { },
/**
* Listen action 'end' event.
* End of action data transmission.
*/
end: function() {
logger.debug('Action ended');
this.out.end();
},
/**
* Set target stream and start outputting.
*/
outputTo: function(headers, stream) {
this.headers = headers;
this.out = stream;
}
};
/**
* Outputter in json format.
*/
var JSONOutputter = function() {
Outputter.call(this);
logger.debug('JSON');
this.buffer = new Array();
this.colStarted = false;
};
// inherits Outputter
JSONOutputter.prototype = Object.create(Outputter.prototype, {
output: {
value: function(resource) {
logger.debug('ressource: '+JSON.stringify(this.buffer));
logger.debug('colStarted: '+this.colStarted);
if (!this.colStarted && this.buffer.length == 0)
this.buffer.push(resource);
else {
this.buffer.push(resource);
if (!this.colStarted) {
this.out.write('[');
this.colStarted = true;
} else
this.out.write(',');
while (this.buffer.length>0) {
var r = this.buffer.shift();
this.out.write(JSON.stringify(r));
if (this.buffer.length>0)
this.out.write(',');
}
}
}, enumerable: true, configurable: true, writable: true
},
end: {
value: function() {
if (this.buffer.length == 1) {
// single resource
this.out.write(JSON.stringify(this.buffer[0]));
}
if (this.colStarted) {
//end collection
this.out.write(']');
}
logger.debug('Action ended');
this.out.end();
}, enumerable: true, configurable: true, writable: true
},
init: {
value: function() {
this.addHeader('Content-Type', 'application/json');
}, enumerable: true, configurable: true, writable: true
}
});
/**
* Outputter in html.
*/
var HtmlOutputter = function() {
Outputter.call(this);
logger.debug('HTML');
// TODO use templates
this.buffer = new Array();
this.colStarted = false;
};
// inherits Outputter
HtmlOutputter.prototype = Object.create(Outputter.prototype, {
output: {
value: function(resource) {
var self = this;
logger.debug('colStarted: '+this.colStarted);
if (!this.colStarted && this.buffer.length == 0) {
this.buffer.push(resource);
this.out.write('<html><head><title>Bouquins</title></head>');
} else {
this.buffer.push(resource);
if (!this.colStarted) {
this.out.write('<h1>Data</h1><table><tr>');
this.colStarted = true;
} else
this.out.write('</tr>');
while (this.buffer.length>0) {
var r = this.buffer.shift();
Object.keys(r).forEach(function(key) {
self.out.write('<td title=\"'+key+'\">'+r[key]+'</td>');
});
if (this.buffer.length>0)
this.out.write('</tr><tr>');
}
}
}, enumerable: true, configurable: true, writable: true
},
end: {
value: function() {
var self = this;
if (this.buffer.length == 1) {
// single resource
this.out.write('<ul>');
var r = this.buffer[0];
Object.keys(r).forEach(function(key) {
self.out.write('<li>'+key+': '+r[key]+'</li>');
});
this.out.write('</ul>');
}
if (this.colStarted) {
//end collection
this.out.write('</table>');
}
// links
var links = this.headers.Link;
if (links) {
this.out.write('<h1>Links</h1><ul>')
var re = /<([^>]*)>; rel=([^,$]*)/g;
var match;
while ((match = re.exec(links)) != null) {
this.out.write('<li><a href=\"'+match[1]+'\">'+match[2]+'</a></li>');
}
this.out.write('</ul>')
}
this.out.write('</html>');
logger.debug('Action ended');
this.out.end();
}, enumerable: true, configurable: true, writable: true
},
init: {
value: function() {
this.addHeader('Content-Type', 'text/html');
}, enumerable: true, configurable: true, writable: true
}
});
/**
* Outputter in text.
*/
var TextOutputter = function() {
Outputter.call(this);
};
// inherits Outputter
TextOutputter.prototype = Object.create(Outputter.prototype, {
});
//module.exports.JSONOutputter = JSONOutputter;
module.exports = {
JSONOutputter: JSONOutputter,
TextOutputter: TextOutputter,
HtmlOutputter: HtmlOutputter
};

View File

@ -1,158 +0,0 @@
/**
* TODO license
* Router class.
*/
var util = require('util')
Endpoint = require('../endpoint/endpoint.js'),
Outputter = require('../outputter/outputter');
exports = module.exports = Router;
function Router() {
// constructor
}
Router.prototype = {
//
// FUNCTIONS
//
/**
* Build the endpoint for given path.
* @param path <string> full path
* @param callback <function(error, endpoint)> callback
*/
buildEndpoint : function (path, callback) {
logger.debug('Building endpoint for ' + path);
var endpoint = null;
var error = null;
if (!config.endpoints) {
error = new Error("no endpoints defined");
} else {
var match = PATH_RE.exec(path);
var resName = match ? match[1] : null;
logger.debug('Match resource : ' + resName);
var resModule = config.endpoints[resName];
if (resModule) {
logger.debug('Loading ' + resModule);
try {
logger.debug('../' + resModule);
endpoint = require('../' + resModule);
} catch (err) {
logger.debug('Error loading module');
error = err;
}
} else {
error = new Error('No module for ' + resName);
}
}
callback(error, endpoint);
},
/**
* Build an outputter for given mime type.
* @param mime <string> requested mime type.
* @param callback <function(err)> error callback
*/
buildOutputter : function(mime, callback) {
var mimes = mime.split(',');
var outputter = null;
for (var i=0; i<mimes.length; i++) {
switch(mimes[i]) {
case 'application/json':
return new Outputter.JSONOutputter();
case 'text/html':
return new Outputter.HtmlOutputter();
case 'text/plain':
return new Outputter.TextOutputter();
}
}
logger.error('Usupported type: '+mime);
return new Outputter.JSONOutputter();
},
/**
* Listener 'request' event.
*/
request: function(req, resp) {
// req headers available
// pause stream until action is ready
req.pause();
logger.debug('Handling request');
// build outputter
var outputter = this.buildOutputter(req.headers.accept, function(err) {
logger.error(err);
resp.writeHead(500, 'outputter failure');
resp.write(err.message);
resp.end();
});
logger.debug('outputter: ' + outputter);
var url = require('url').parse(req.url, true);
// TODO sanitize url.pathname
this.buildEndpoint(url.pathname, function(err, endpoint) {
if (err) {
logger.error(err);
resp.writeHead(404, 'no endpoint for ' + url.pathname);
resp.write(err.message);
resp.end();
return;
}
endpoint.buildAction(req.method, url, function(err, action) {
//TODO err
// allow outputter to set headers
outputter.addHeader = function(name, value) {
if (resp.headersSent) {
logger.error('Header already sent, ignoring: ' + name);
} else {
resp.setHeader(name, value);
}
};
if (action.withReqData()) {
// action needs all request data
// listen data event
req.on('data', action.reqData);
}
// when request data received, prepare action, send headers, exec action
req.on('end', function() {
outputter.init();
action.prepare(function(err, statusCode, headers){
//TODO err
// TODO does it keep outputter headers?
resp.writeHead(statusCode, headers);
// wire streaming event
action.on('data', function(chunk) {
outputter.output(chunk);
});
action.on('end', function() {
outputter.end();
});
// start outputter
outputter.outputTo(headers, resp);
// start action
action.doAction();
});
});
// resume reading request stream
req.resume();
});
});
}
};

View File

@ -1,22 +0,0 @@
/**
* Config module.
* TODO license
*/
var config = exports;
/**
* Loads config from config file
*/
config.loadconfig=function(configfile, callback) {
require('fs').readFile(
configfile, {encoding:'utf8'},
function(err, data) {
if (err) callback(err);
try {
var config = JSON.parse(data);
callback(null, config);
} catch (err) {
callback(err);
}
});
};

View File

@ -1,28 +0,0 @@
/**
* TODO license
* Basic logger.
*/
var logger = exports;
logger.debugLevel = 'debug';
logger.log = function(level, message) {
var levels = ['fatal', 'error', 'warn', 'info', 'debug'];
if (levels.indexOf(level) <= levels.indexOf(logger.debugLevel) ) {
if (typeof message !== 'string') {
message = JSON.stringify(message);
};
console.log(new Date().toISOString() + ' [' + level+'] '+message);
}
}
logger.debug = function(message) {
logger.log('debug', message);
}
logger.info = function(message) {
logger.log('info', message);
}
logger.error = function(message) {
logger.log('error', message);
}
logger.fatal = function(message) {
logger.log('fatal', message);
}

View File

@ -3,7 +3,10 @@
"description" : "HTTP frontend for calibre", "description" : "HTTP frontend for calibre",
"url" : "TODO", "url" : "TODO",
"author" : "Meutel <meutel@meutel.net>", "author" : "Meutel <meutel@meutel.net>",
"dependencies" : {}, "private" : true,
"dependencies" : {
"express": "3.x"
},
"main" : "./lib/bouquins", "main" : "./lib/bouquins",
"bin" : { "bootstrap" : "./bin/bootstrap" }, "bin" : { "bootstrap" : "./bin/bootstrap" },
"version" : "0.1.0" "version" : "0.1.0"