walnut.js/lib/services-loader.js

118 lines
3.3 KiB
JavaScript

'use strict';
module.exports.create = function (conf, deps) {
var PromiseA = deps.Promise;
function loadService(node) {
var path = require('path');
return new PromiseA(function (resolve) {
// process.nextTick runs at the end of the current event loop
// we actually want time to pass so that potential api traffic can be handled
setTimeout(function () {
var servicepath = path.join(conf.servicespath, node);
var pkg;
try {
// TODO no package should be named package.json
pkg = require(servicepath + '/package.json');
resolve({
pkg: pkg
, name: node
, service: require(servicepath)
});
return;
} catch(e) {
// TODO report errors to admin console
// TODO take sha256sum of e.stack and store in db with tick for updatedAt
console.error("[Service] could not require service '" + servicepath + "'");
console.error(e.stack);
//services.push({ error: e });
resolve(null);
return;
}
}, 1);
});
}
function loadServices() {
var fs = PromiseA.promisifyAll(require('fs'));
// deps : { memstore, sqlstores, clientSqlFactory, systemSqlFactory }
// XXX this is a no-no (file system access in a worker, cannot be statically analyzed)
// TODO regenerate a static file of all requires on each install
// TODO read system config db to find which services auto-start
// TODO allow certain apis access to certain services
return fs.readdirAsync(conf.servicespath).then(function (nodes) {
var promise = PromiseA.resolve();
var services = [];
nodes.forEach(function (node) {
if (/^\./.test(node)) {
// ignore dot files
return;
}
promise = promise.then(function () {
return loadService(node).then(function (srv) {
if (!srv) {
return;
}
services.push(srv);
});
});
});
return promise.then(function () {
return services;
});
});
}
function startService(srv) {
return new PromiseA(function (resolve) {
// process.nextTick runs at the end of the current event loop
// we actually want time to pass so that potential api traffic can be handled
setTimeout(function () {
try {
PromiseA.resolve(srv.service.create(conf, deps)).then(resolve, function (e) {
console.error("[Service] couldn't promise service");
console.error(e.stack);
resolve(null);
});
return;
} catch(e) {
console.error("[Service] couldn't start service");
console.error(e.stack);
resolve(null);
return;
}
}, 1);
});
}
function startServices(services) {
var promise = PromiseA.resolve();
var servicesMap = {};
services.forEach(function (srv) {
promise = promise.then(function () {
return startService(srv).then(function (service) {
if (!service) {
// TODO log
return null;
}
srv.service = service;
servicesMap[srv.name] = srv;
});
});
});
return promise.then(function () {
return servicesMap;
});
}
return loadServices().then(startServices);
};