UNPKG

nsolid-manager

Version:

A manager that sets up and runs N|Solid for you

177 lines (151 loc) 5.29 kB
/** * 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