smart-home
Version:
Netbeast dashboard, IoT apps manager
153 lines (122 loc) • 3.82 kB
JavaScript
var path = require('path')
var events = require('events')
var spawn = require('child_process').spawn
var freeport = require('freeport')
var chalk = require('chalk')
var request = require('superagent')
var mqtt = require('mqtt')
var broker = require('../helpers/broker')
var ApiError = require('../util/api-error')
var Resource = require('./resource')
var App = require('./app')
const APPS_DIR = process.env.APPS_DIR
// Apps with their child object running
var children = {}
var self = module.exports = new events.EventEmitter()
var client = mqtt.connect() // for notifications
self.status = function (req, res, next) {
var child = children[req.params.name]
if (!child) return res.json({ name: req.params.name, port: -1, status: 'Not running' })
self.ready(child, function (err, act) {
if (err) return next(err)
res.json({ name: act.name, port: act.port })
})
}
self.stop = function (appName, done) {
if (children[appName]) {
console.log('Sending SIGTERM to ' + appName)
children[appName].process.kill('SIGTERM')
children[appName] = undefined
}
done()
}
self.get = function (appName) {
return children[appName]
}
self.all = function (done) {
App.modules(function (err, apps) {
if (err) return done(err)
apps = apps.filter(function (app) {
return children[app.name]
})
done(null, apps)
})
}
self.ready = function (child, done) {
if (child.ready) return done(null, child)
const APP_URL = 'http://localhost:' + child.port
const MAX_TRIALS = 20
var k = 0
;(function keepTrying () {
if (k >= MAX_TRIALS) return done(new ApiError(405, 'Impossible to launch the application'))
request(APP_URL).end(function (err, resp) {
if (err && err.code === 'ECONNREFUSED') {
k++
return setTimeout(keepTrying, 400)
} else if (!err || err.status === 404) {
child.ready = true
return done(null, child)
} else {
console.log(err)
return done(err)
}
})
})()
}
self.boot = function (appName, done) {
var child = { name: appName }
if (children[appName]) {
return done(null, children[child.name])
}
freeport(function (err, port) {
if (err) {
done(new ApiError(422, 'Not enough ports'))
} else {
child.port = port
self.emit('start', child)
done(null, child)
}
})
}
self.on('start', function (app) {
if (children[app.name]) return
App.getPackageJson(app.name, function (err, pkgJson) {
if (err) return broker.error(err.toString())
// *****************
// Child management
// *****************
var entryPoint = path.resolve(APPS_DIR, app.name, pkgJson.main)
var env = Object.create(process.env)
env.APP_PORT = app.port
env.APP_NAME = app.name
env.NETBEAST = process.env.IPs.split(',')[0] + ':' + process.env.PORT
var child = spawn('node', [entryPoint, '--port', app.port], {
cwd: path.join(APPS_DIR, app.name),
env: env
})
child.stdout.on('data', function (data) {
process.stdout.write(chalk.grey('[' + app.name + '] '+ data))
})
child.stderr.on('data', function (data) {
process.stdout.write(chalk.red('[' + app.name + '] '+ data))
broker.error(data.toString(), app.name)
})
child.on('close', function (code) {
client.publish('netbeast/activities/close', app.name)
Resource.destroy({ app: app.name })
children[app.name] = undefined
})
child.on('error', function (error) {
broker.error(' exited with code ' + error || 0, app.name)
console.trace(error)
children[app.name] = undefined
})
app.process = child
children[app.name] = app
})
})
process.on('exit', function () {
for (var key in children) {
children[key].process.kill('SIGTERM')
}
})