nsolid-manager
Version:
A manager that sets up and runs N|Solid for you
177 lines (151 loc) • 5.29 kB
JavaScript
/**
* Server-side lib for collect
*/
var url = require('url')
var path = require('path')
var fs = require('fs')
var mkdirp = require('mkdirp')
var spawn = require('child_process').spawn
var split = require('split')
var createServerRoutes = require('./collect/routes')
var st = require('st')
var skateboard = require('skateboard')
var graphCache = require('./collect/graph-cache')
var poll = require('./collect/poll')
var defaultNodeEnv = 'production'
try {
var stat = fs.statSync(path.join(__dirname, '..', 'webpack.config.js'))
if (stat) {
defaultNodeEnv = 'development'
}
} catch (e) {}
// webpack dev/hot middleware configuration
var developmentMode = (process.env.NODE_ENV || defaultNodeEnv) !== 'production'
function bailOnError (message, fn, context) {
return function () {
console.error(message)
try {
return fn.apply(context, arguments)
} catch (e) {
console.log(e.stack)
}
}
}
function createServer (options, pollingEmitter, cache, error) {
// sanity-check that we can report errors
if (typeof error !== 'function') throw new Error('error handler must be a function!')
if (developmentMode) {
var webpackConfig = require('../webpack.config')
var webpack = require('webpack')
var compiler = webpack(webpackConfig)
var webpackDevMiddleware = require('webpack-dev-middleware')(compiler, webpackConfig.middleware)
var webpackHotMiddleware = require('webpack-hot-middleware')(compiler, webpackConfig.hotMiddleware)
}
function processMiddleware (req, res, fn) {
// only run the middleware when developmentMode is enabled
if (developmentMode) {
webpackDevMiddleware(req, res, function (err) {
if (err) return fn(err)
webpackHotMiddleware(req, res, fn)
})
} else {
fn()
}
}
var port = options.port
var iface = options.interface
var storagePath = options.storage
var workerPath = path.join(__dirname, 'collect', 'heap-snapshot', 'worker.js')
var router
var applicationClients = []
if (!storagePath) return error(new Error('storage path required'))
var snapshotPath = path.join(storagePath, 'snapshots')
var profilePath = path.join(storagePath, 'profiles')
try {
mkdirp.sync(snapshotPath)
mkdirp.sync(profilePath)
} catch (err) {
return error(err)
}
router = createServerRoutes(storagePath, cache)
var mount = st({
path: path.join(__dirname, '..', developmentMode ? 'assets' : 'build'),
index: developmentMode ? false : 'index.html',
passthrough: true
})
function handleRequest (req, res) {
processMiddleware(req, res, function () {
var urlPath = url.parse(req.url).pathname
var match = router && router.match(urlPath)
if (match) { // serve dynamic routes such as profiles and heap snapshots
match.fn(req, res, match)
} else { // serve static files
mount(req, res, function () {
req.url = '/'
// `st` has cached the url as a 404, so override the cache
req.sturl = '/'
processMiddleware(req, res, function () {
mount(req, res, function () {
res.writeHead(404, 'Not Found')
res.end('Not Found')
})
})
})
}
})
}
pollingEmitter.on('output', function (output) {
cache.save(err => {
if (err) console.error(err.stack || err)
})
applicationClients.forEach(function (client) {
var graphs = graphCache.buildGraphPayload(client)
client.write(JSON.stringify(Object.assign({}, output, graphs)) + '\n')
})
})
return skateboard({
port: port,
interface: iface,
dir: path.join(__dirname, '..', developmentMode ? 'assets' : 'build'),
requestHandler: handleRequest,
staticHandler: mount
}, function (stream, params) {
if (params.source === 'applications') {
// keep track of connected clients
applicationClients.push(stream)
stream.on('end', function () {
applicationClients = applicationClients.filter(function (a) {
return a !== stream
})
})
stream.write(JSON.stringify(cache.toJSON()) + '\n')
stream.pipe(split()).on('data', function (line) {
if (!line.trim().length) {
return
}
var subscriptions = JSON.parse(line)
// The only thing we care about right now is graph subscriptions
graphCache.updateGraphSubscriptions(stream, subscriptions, function (e, d) {
if (e || !d) {
return
}
var graphs = graphCache.buildGraphPayload(stream)
stream.write(JSON.stringify(Object.assign(cache.toJSON(), graphs)) + '\n')
})
})
} else if (params.source === 'heapsnapshot') {
var proc = spawn('node', [workerPath, '--dir=' + snapshotPath], {
stdio: 'pipe'
})
stream.once('end', bailOnError('client disconnected from heapsnapshot worker', proc.kill, proc))
proc.on('exit', bailOnError('heapsnapshot worker exited', stream.close, stream))
proc.stderr.pipe(process.stderr)
stream.pipe(proc.stdin)
proc.stdout.setEncoding('utf8')
proc.stdout.pipe(stream)
}
})
}
// INTERFACE
module.exports.poll = poll
module.exports.createServer = createServer