<commit_before>const bodyParser = require('body-parser');
const controller = require('./controller');

module.exports = (config) => {
  var session = require('./controller/library/session')(config);
  var cookie = require('./controller/library/cookie')(config);

  var bodyParserUrlEncodedOpts = {extended: true};

  if (config.cfg.requestSizeLimit != undefined) {
    bodyParserUrlEncodedOpts.limit = config.cfg.requestSizeLimit;
  }

  var bodyParserUrlEncoded = bodyParser.urlencoded(bodyParserUrlEncodedOpts);

  var handler = (delegate, req, res) => {
    return async (() => {
      var ctrl = controller.delegate(config, req, res, session, cookie);
      return await (delegate(ctrl));
    })()
  }

  var delegates = {
    route: (method, path, delegate, middlewares, middlewaresEnd) => {
      var params = [path];
      var fn = undefined;

      if (method == 'get') {
        fn = config.app.get;
      }
      else if (method == 'post') {
        fn = config.app.post;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'delete') {
        fn = config.app.delete;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'put') {
        fn = config.app.put;
        params.push(bodyParserUrlEncoded);
      }

      // Add middlewares
      if ((middlewares && typeof(middlewares) === 'object') ||
          (middlewaresEnd && typeof(middlewaresEnd) === 'object')) {
        params.push((req, res, next) => {
          var dp = (callback) => {
            return async(() => {
              var ctrl = controller.delegate(config, req, res, session, cookie);
              var handler = {
                controller: ctrl,
                helper: config.helper,
                model: config.model
              };

              try {
                await (callback(handler))
              }
              catch (err) {
                controller.handler.serverError(ctrl, req, res, config, err, 500);
              }
            })();
          };

          req.async = dp;
          res.async = dp;

          next();
        });
      }

      // Add middlewares
      if (middlewares && typeof(middlewares) === 'object') {
        middlewares.forEach((middleware) => {
          params.push(middleware);
        });
      }

      var isAvailableMiddlewaresForEnd = middlewaresEnd && typeof(middlewaresEnd) === 'object';

      var finalizeMiddleware = (req, res, next) => {
        if (res.buffer.body === undefined) {
          res.status(204).send();
        }
        else {
          res.status(res.buffer.code).send(res.buffer.body);
        }
      };

      // Add action
      params.push((req, res, next) => {
        var done = handler(delegate, req, res, next);

        done.then((resp) => {
          if (typeof(res.buffer) === 'undefined' && resp) {
            res.buffer = {
              code: 200,
              body: resp
            };
          }
          else if (resp === true && isAvailableMiddlewaresForEnd) {
            finalizeMiddleware(req, res);
            return;
          }

          next();
        }).catch((err) => {
          var ctrl = controller.delegate(config, req, res, session, cookie);
          controller.handler.serverError(ctrl, req, res, config, err, 500);
        });
      });

      // Add middlewares for end
      if (isAvailableMiddlewaresForEnd) {
        middlewaresEnd.forEach((middleware) => {
          params.push(middleware);
        });
      }

      params.push(finalizeMiddleware);

      // Error handling globally.
      params.push((err, req, res, next) => {
        var ctrl = controller.delegate(config, req, res, session, cookie);
        controller.handler.serverError(ctrl, req, res, config, err, 500);
      });

      if (fn) {
        fn.apply(config.app, params);
      }
    }
  }

  if (config.cfg.controller) {
    var fs = require('fs');
    var trees = [];
    var globalMiddlewaresFirst = [];
    var globalMiddlewaresEnd = [];

    var traverse = (path, dirPath) => {
      try {
        if (!fs.statSync(dirPath).isDirectory()) {
          return;
        }
      } catch(_) {
        console.error('Controller path load error, ' + dirPath);
        return;
      }

      var files = fs.readdirSync(dirPath);

      files.sort((a, b) => {
        if (a.indexOf('_.') === 0 || a.indexOf('__.') === 0) return -1;
        return 1;
      });

      files.forEach(function(name) {
        var fullPath = dirPath + '/' + name

        if (name.startsWith('.')) { return }

        if (fs.statSync(fullPath).isDirectory()) {
          traverse(path + '/' + name, fullPath)
        }
        else {
          includePreapre(
            name != 'index.js' ? path + '/' + name.substr(0, name.lastIndexOf('.')) : path,
            fullPath
          );
        }
      })
    };

    var findMiddleware = (path, isFirst) => {
      var middlewares;

      if (isFirst) {
        middlewares = globalMiddlewaresFirst;
      }
      else {
        middlewares = globalMiddlewaresEnd;
      }

      for (var i = 0; i < middlewares.length; i++) {
        if (path.indexOf(middlewares[i][0]) === 0 &&
            (path === middlewares[i][0] ||
             path.slice(middlewares[i][0].length, middlewares[i][0].length + 1) === '/')) {
          return middlewares[i][1];
        }
      }

      return null;
    }

    var includePreapre = (path, filePath) => {
      if (!fs.statSync(filePath).isFile()) { return false }

      // Middleware for begin
      if (filePath.lastIndexOf('/_.') === filePath.length - 5) {
        var middleware = require(filePath);
        globalMiddlewaresFirst.unshift([path.slice(0, -2), middleware]);
        return;
      }
      // Middleware for end
      else if (filePath.lastIndexOf('/__.') === filePath.length - 6) {
        var middleware = require(filePath);
        globalMiddlewaresEnd.unshift([path.slice(0, -3), middleware]);
        return;
      }

      var routes = require(filePath)
      var methods = ['get', 'post', 'delete', 'put']

      methods.forEach((method) => {
        if (typeof routes[method] == 'function') {
          var replaceAll = routes.path || routes['_'];
          var replaceMethod = routes[method + 'Path'] ||
                              routes['_' + method];  // getPath, _get
          var suffix = routes[method + 'Suffix'] ||
                       routes[method + '_'];  // getSuffix, get_
          var middlewareForGlobal = findMiddleware(path, true);
          var middlewareForGlobalEnd = findMiddleware(path, false);
          var middlewaresForAll = routes['middlewares'] ||
                                  routes['middleware'] ||
                                  routes['__'];
          var middlewares = routes[method + 'Middlewares'] ||
                            routes[method + 'Middleware'] ||
                            routes['_' + method + '_'];  // getMiddlewares, getMiddleware, _get_

          if (middlewareForGlobal && typeof(middlewareForGlobal) === 'function') {
            middlewareForGlobal = [middlewareForGlobal];
          }

          if (middlewareForGlobalEnd && typeof(middlewareForGlobalEnd) === 'function') {
            middlewareForGlobalEnd = [middlewareForGlobalEnd];
          }

          if (middlewaresForAll && typeof(middlewaresForAll) === 'function') {
            middlewaresForAll = [middlewaresForAll];
          }

          if (middlewares && typeof(middlewares) === 'function') {
            middlewares = [middlewares];
          }

          var middleware = middlewares || middlewaresForAll || middlewareForGlobal;

          path = replaceAll || replaceMethod || path;
          path = path + (suffix ? suffix : '');

          trees.push([method, path, routes[method], middleware, middlewareForGlobalEnd]);
        }
      })
    };

    var includes = () => {
      var pp = [];  // Only paths
      var ps = [];  // Parameter paths

      // More specific paths are first.
      // /foo/bar is high priority than /foo
      // /foo/bar is high priority than /foo/:type

      trees.forEach((e, k) => {
        if (e[1].indexOf(':') === -1) pp.push(k);
        else ps.push(k);
      });

      pp.sort((a, b) => {
        var a1 = trees[a][1].split('/');
        var b1 = trees[b][1].split('/');

        if (a1.length > b1.length) return -1;
        if (a1.length < b1.length) return 1;

        if (trees[a][1] == trees[b][1]) return 0;
        if (trees[a][1] > trees[b][1]) return -1;
        return 1;
      });

      ps.sort((a, b) => {
        var a1 = trees[a][1].split('/');
        var b1 = trees[b][1].split('/');

        if (a1.length > b1.length) return -1;
        if (a1.length < b1.length) return 1;

        if (trees[a][1] == trees[b][1]) return 0;
        if (trees[a][1] > trees[b][1]) return -1;
        return 1;
      });

      pp.concat(ps).forEach((e) => {
        delegates.route.apply(this, trees[e]);
      });
    };

    traverse('', config.cfg.controller);
    includes();

    config.app.use((req, res, next) => {
      var ctrl = controller.delegate(config, req, res, session, cookie);
      controller.handler.serverError(ctrl, req, res, config, null, 404);
    });
  }

  return delegates
}
<commit_msg>fixed route rule error<commit_after>const bodyParser = require('body-parser');
const controller = require('./controller');

module.exports = (config) => {
  var session = require('./controller/library/session')(config);
  var cookie = require('./controller/library/cookie')(config);

  var bodyParserUrlEncodedOpts = {extended: true};

  if (config.cfg.requestSizeLimit != undefined) {
    bodyParserUrlEncodedOpts.limit = config.cfg.requestSizeLimit;
  }

  var bodyParserUrlEncoded = bodyParser.urlencoded(bodyParserUrlEncodedOpts);

  var handler = (delegate, req, res) => {
    return async (() => {
      var ctrl = controller.delegate(config, req, res, session, cookie);
      return await (delegate(ctrl));
    })()
  }

  var delegates = {
    route: (method, path, delegate, middlewares, middlewaresEnd) => {
      var params = [path];
      var fn = undefined;

      if (method == 'get') {
        fn = config.app.get;
      }
      else if (method == 'post') {
        fn = config.app.post;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'delete') {
        fn = config.app.delete;
        params.push(bodyParserUrlEncoded);
      }
      else if (method == 'put') {
        fn = config.app.put;
        params.push(bodyParserUrlEncoded);
      }

      // Add middlewares
      if ((middlewares && typeof(middlewares) === 'object') ||
          (middlewaresEnd && typeof(middlewaresEnd) === 'object')) {
        params.push((req, res, next) => {
          var dp = (callback) => {
            return async(() => {
              var ctrl = controller.delegate(config, req, res, session, cookie);
              var handler = {
                controller: ctrl,
                helper: config.helper,
                model: config.model
              };

              try {
                await (callback(handler))
              }
              catch (err) {
                controller.handler.serverError(ctrl, req, res, config, err, 500);
              }
            })();
          };

          req.async = dp;
          res.async = dp;

          next();
        });
      }

      // Add middlewares
      if (middlewares && typeof(middlewares) === 'object') {
        middlewares.forEach((middleware) => {
          params.push(middleware);
        });
      }

      var isAvailableMiddlewaresForEnd = middlewaresEnd && typeof(middlewaresEnd) === 'object';

      var finalizeMiddleware = (req, res, next) => {
        if (res.buffer.body === undefined) {
          res.status(204).send();
        }
        else {
          res.status(res.buffer.code).send(res.buffer.body);
        }
      };

      // Add action
      params.push((req, res, next) => {
        var done = handler(delegate, req, res, next);

        done.then((resp) => {
          if (typeof(res.buffer) === 'undefined' && resp) {
            res.buffer = {
              code: 200,
              body: resp
            };
          }
          else if (resp === true && isAvailableMiddlewaresForEnd) {
            finalizeMiddleware(req, res);
            return;
          }

          next();
        }).catch((err) => {
          var ctrl = controller.delegate(config, req, res, session, cookie);
          controller.handler.serverError(ctrl, req, res, config, err, 500);
        });
      });

      // Add middlewares for end
      if (isAvailableMiddlewaresForEnd) {
        middlewaresEnd.forEach((middleware) => {
          params.push(middleware);
        });
      }

      params.push(finalizeMiddleware);

      // Error handling globally.
      params.push((err, req, res, next) => {
        var ctrl = controller.delegate(config, req, res, session, cookie);
        controller.handler.serverError(ctrl, req, res, config, err, 500);
      });

      if (fn) {
        fn.apply(config.app, params);
      }
    }
  }

  if (config.cfg.controller) {
    var fs = require('fs');
    var trees = [];
    var globalMiddlewaresFirst = [];
    var globalMiddlewaresEnd = [];

    var traverse = (path, dirPath) => {
      try {
        if (!fs.statSync(dirPath).isDirectory()) {
          return;
        }
      } catch(_) {
        console.error('Controller path load error, ' + dirPath);
        return;
      }

      var files = fs.readdirSync(dirPath);

      files.sort((a, b) => {
        if (a.indexOf('_.') === 0 || a.indexOf('__.') === 0) return -1;
        return 1;
      });

      files.forEach(function(name) {
        var fullPath = dirPath + '/' + name

        if (name.startsWith('.')) { return }

        if (fs.statSync(fullPath).isDirectory()) {
          traverse(path + '/' + name, fullPath)
        }
        else {
          includePreapre(
            name != 'index.js' ? path + '/' + name.substr(0, name.lastIndexOf('.')) : path,
            fullPath
          );
        }
      })
    };

    var findMiddleware = (path, isFirst) => {
      var middlewares;

      if (isFirst) {
        middlewares = globalMiddlewaresFirst;
      }
      else {
        middlewares = globalMiddlewaresEnd;
      }

      for (var i = 0; i < middlewares.length; i++) {
        if (path.indexOf(middlewares[i][0]) === 0 &&
            (path === middlewares[i][0] ||
             path.slice(middlewares[i][0].length, middlewares[i][0].length + 1) === '/')) {
          return middlewares[i][1];
        }
      }

      return null;
    }

    var includePreapre = (path, filePath) => {
      if (!fs.statSync(filePath).isFile()) { return false }

      // Middleware for begin
      if (filePath.lastIndexOf('/_.') === filePath.length - 5) {
        var middleware = require(filePath);
        globalMiddlewaresFirst.unshift([path.slice(0, -2), middleware]);
        return;
      }
      // Middleware for end
      else if (filePath.lastIndexOf('/__.') === filePath.length - 6) {
        var middleware = require(filePath);
        globalMiddlewaresEnd.unshift([path.slice(0, -3), middleware]);
        return;
      }

      var routes = require(filePath)
      var methods = ['get', 'post', 'delete', 'put']

      methods.forEach((method) => {
        if (typeof routes[method] == 'function') {
          var replaceAll = routes.path || routes['_'];
          var replaceMethod = routes[method + 'Path'] ||
                              routes['_' + method];  // getPath, _get
          var suffix = routes[method + 'Suffix'] ||
                       routes[method + '_'];  // getSuffix, get_
          var middlewareForGlobal = findMiddleware(path, true);
          var middlewareForGlobalEnd = findMiddleware(path, false);
          var middlewaresForAll = routes['middlewares'] ||
                                  routes['middleware'] ||
                                  routes['__'];
          var middlewares = routes[method + 'Middlewares'] ||
                            routes[method + 'Middleware'] ||
                            routes['_' + method + '_'];  // getMiddlewares, getMiddleware, _get_

          if (middlewareForGlobal && typeof(middlewareForGlobal) === 'function') {
            middlewareForGlobal = [middlewareForGlobal];
          }

          if (middlewareForGlobalEnd && typeof(middlewareForGlobalEnd) === 'function') {
            middlewareForGlobalEnd = [middlewareForGlobalEnd];
          }

          if (middlewaresForAll && typeof(middlewaresForAll) === 'function') {
            middlewaresForAll = [middlewaresForAll];
          }

          if (middlewares && typeof(middlewares) === 'function') {
            middlewares = [middlewares];
          }

          var middleware = middlewares || middlewaresForAll || middlewareForGlobal;

          var routePath = replaceAll || replaceMethod || path;
          routePath = routePath + (suffix ? suffix : '');

          trees.push([method, routePath, routes[method], middleware, middlewareForGlobalEnd]);
        }
      })
    };

    var includes = () => {
      var pp = [];  // Only paths
      var ps = [];  // Parameter paths

      // More specific paths are first.
      // /foo/bar is high priority than /foo
      // /foo/bar is high priority than /foo/:type

      trees.forEach((e, k) => {
        if (e[1].indexOf(':') === -1) pp.push(k);
        else ps.push(k);
      });

      pp.sort((a, b) => {
        var a1 = trees[a][1].split('/');
        var b1 = trees[b][1].split('/');

        if (a1.length > b1.length) return -1;
        if (a1.length < b1.length) return 1;

        if (trees[a][1] == trees[b][1]) return 0;
        if (trees[a][1] > trees[b][1]) return -1;
        return 1;
      });

      ps.sort((a, b) => {
        var a1 = trees[a][1].split('/');
        var b1 = trees[b][1].split('/');

        if (a1.length > b1.length) return -1;
        if (a1.length < b1.length) return 1;

        if (trees[a][1] == trees[b][1]) return 0;
        if (trees[a][1] > trees[b][1]) return -1;
        return 1;
      });

      pp.concat(ps).forEach((e) => {
        delegates.route.apply(this, trees[e]);
      });
    };

    traverse('', config.cfg.controller);
    includes();

    config.app.use((req, res, next) => {
      var ctrl = controller.delegate(config, req, res, session, cookie);
      controller.handler.serverError(ctrl, req, res, config, null, 404);
    });
  }

  return delegates
}
<|endoftext|>


<commit_before>console.time('init')

var electron = require('electron')

var app = electron.app
var ipcMain = electron.ipcMain

var announcement = require('./announcement')
var config = require('../config')
var crashReporter = require('../crash-reporter')
var dialog = require('./dialog')
var dock = require('./dock')
var handlers = require('./handlers')
var ipc = require('./ipc')
var log = require('./log')
var menu = require('./menu')
var squirrelWin32 = require('./squirrel-win32')
var tray = require('./tray')
var updater = require('./updater')
var userTasks = require('./user-tasks')
var windows = require('./windows')

var shouldQuit = false
var argv = sliceArgv(process.argv)

if (config.IS_PRODUCTION) {
  // When Electron is running in produdtion mode (packaged app), then run React
  // in production mode too.
  process.env.NODE_ENV = 'production'
}

if (process.platform === 'win32') {
  shouldQuit = squirrelWin32.handleEvent(argv[0])
  argv = argv.filter((arg) => !arg.includes('--squirrel'))
}

if (!shouldQuit) {
  // Prevent multiple instances of app from running at same time. New instances signal
  // this instance and quit.
  shouldQuit = app.makeSingleInstance(onAppOpen)
  if (shouldQuit) {
    app.quit()
  }
}

if (!shouldQuit) {
  init()
}

function init () {
  if (config.IS_PORTABLE) {
    app.setPath('userData', config.CONFIG_PATH)
  }

  var isReady = false // app ready, windows can be created
  app.ipcReady = false // main window has finished loading and IPC is ready
  app.isQuitting = false

  // Open handlers must be added as early as possible
  app.on('open-file', onOpen)
  app.on('open-url', onOpen)

  ipc.init()

  app.once('will-finish-launching', function () {
    crashReporter.init()
  })

  app.on('ready', function () {
    isReady = true

    windows.main.init()
    windows.webtorrent.init()
    menu.init()

    // To keep app startup fast, some code is delayed.
    setTimeout(delayedInit, config.DELAYED_INIT)

    // Report uncaught exceptions
    process.on('uncaughtException', (err) => {
      console.error(err)
      var errJSON = {message: err.message, stack: err.stack}
      windows.main.dispatch('uncaughtError', 'main', errJSON)
    })
  })

  app.once('ipcReady', function () {
    log('Command line args:', argv)
    processArgv(argv)
    console.timeEnd('init')
  })

  app.on('before-quit', function (e) {
    if (app.isQuitting) return

    app.isQuitting = true
    e.preventDefault()
    windows.main.dispatch('saveState') // try to save state on exit
    ipcMain.once('savedState', () => app.quit())
    setTimeout(() => {
      console.error('Saving state took too long. Quitting.')
      app.quit()
    }, 2000) // quit after 2 secs, at most
  })

  app.on('activate', function () {
    if (isReady) windows.main.show()
  })
}

function delayedInit () {
  announcement.init()
  dock.init()
  handlers.install()
  tray.init()
  updater.init()
  userTasks.init()
}

function onOpen (e, torrentId) {
  e.preventDefault()

  if (app.ipcReady) {
    // Magnet links opened from Chrome won't focus the app without a setTimeout.
    // The confirmation dialog Chrome shows causes Chrome to steal back the focus.
    // Electron issue: https://github.com/atom/electron/issues/4338
    setTimeout(() => windows.main.show(), 100)

    processArgv([ torrentId ])
  } else {
    argv.push(torrentId)
  }
}

function onAppOpen (newArgv) {
  newArgv = sliceArgv(newArgv)

  if (app.ipcReady) {
    log('Second app instance opened, but was prevented:', newArgv)
    windows.main.show()

    processArgv(newArgv)
  } else {
    argv.push(...newArgv)
  }
}

function sliceArgv (argv) {
  return argv.slice(config.IS_PRODUCTION ? 1 : 2)
}

function processArgv (argv) {
  var torrentIds = []
  argv.forEach(function (arg) {
    if (arg === '-n') {
      dialog.openSeedDirectory()
    } else if (arg === '-o') {
      dialog.openTorrentFile()
    } else if (arg === '-u') {
      dialog.openTorrentAddress()
    } else if (arg.startsWith('-psn')) {
      // Ignore Mac launchd "process serial number" argument
      // Issue: https://github.com/feross/webtorrent-desktop/issues/214
    } else {
      torrentIds.push(arg)
    }
  })
  if (torrentIds.length > 0) {
    windows.main.dispatch('onOpen', torrentIds)
  }
}
<commit_msg>fix typo<commit_after>console.time('init')

var electron = require('electron')

var app = electron.app
var ipcMain = electron.ipcMain

var announcement = require('./announcement')
var config = require('../config')
var crashReporter = require('../crash-reporter')
var dialog = require('./dialog')
var dock = require('./dock')
var handlers = require('./handlers')
var ipc = require('./ipc')
var log = require('./log')
var menu = require('./menu')
var squirrelWin32 = require('./squirrel-win32')
var tray = require('./tray')
var updater = require('./updater')
var userTasks = require('./user-tasks')
var windows = require('./windows')

var shouldQuit = false
var argv = sliceArgv(process.argv)

if (config.IS_PRODUCTION) {
  // When Electron is running in production mode (packaged app), then run React
  // in production mode too.
  process.env.NODE_ENV = 'production'
}

if (process.platform === 'win32') {
  shouldQuit = squirrelWin32.handleEvent(argv[0])
  argv = argv.filter((arg) => !arg.includes('--squirrel'))
}

if (!shouldQuit) {
  // Prevent multiple instances of app from running at same time. New instances signal
  // this instance and quit.
  shouldQuit = app.makeSingleInstance(onAppOpen)
  if (shouldQuit) {
    app.quit()
  }
}

if (!shouldQuit) {
  init()
}

function init () {
  if (config.IS_PORTABLE) {
    app.setPath('userData', config.CONFIG_PATH)
  }

  var isReady = false // app ready, windows can be created
  app.ipcReady = false // main window has finished loading and IPC is ready
  app.isQuitting = false

  // Open handlers must be added as early as possible
  app.on('open-file', onOpen)
  app.on('open-url', onOpen)

  ipc.init()

  app.once('will-finish-launching', function () {
    crashReporter.init()
  })

  app.on('ready', function () {
    isReady = true

    windows.main.init()
    windows.webtorrent.init()
    menu.init()

    // To keep app startup fast, some code is delayed.
    setTimeout(delayedInit, config.DELAYED_INIT)

    // Report uncaught exceptions
    process.on('uncaughtException', (err) => {
      console.error(err)
      var errJSON = {message: err.message, stack: err.stack}
      windows.main.dispatch('uncaughtError', 'main', errJSON)
    })
  })

  app.once('ipcReady', function () {
    log('Command line args:', argv)
    processArgv(argv)
    console.timeEnd('init')
  })

  app.on('before-quit', function (e) {
    if (app.isQuitting) return

    app.isQuitting = true
    e.preventDefault()
    windows.main.dispatch('saveState') // try to save state on exit
    ipcMain.once('savedState', () => app.quit())
    setTimeout(() => {
      console.error('Saving state took too long. Quitting.')
      app.quit()
    }, 2000) // quit after 2 secs, at most
  })

  app.on('activate', function () {
    if (isReady) windows.main.show()
  })
}

function delayedInit () {
  announcement.init()
  dock.init()
  handlers.install()
  tray.init()
  updater.init()
  userTasks.init()
}

function onOpen (e, torrentId) {
  e.preventDefault()

  if (app.ipcReady) {
    // Magnet links opened from Chrome won't focus the app without a setTimeout.
    // The confirmation dialog Chrome shows causes Chrome to steal back the focus.
    // Electron issue: https://github.com/atom/electron/issues/4338
    setTimeout(() => windows.main.show(), 100)

    processArgv([ torrentId ])
  } else {
    argv.push(torrentId)
  }
}

function onAppOpen (newArgv) {
  newArgv = sliceArgv(newArgv)

  if (app.ipcReady) {
    log('Second app instance opened, but was prevented:', newArgv)
    windows.main.show()

    processArgv(newArgv)
  } else {
    argv.push(...newArgv)
  }
}

function sliceArgv (argv) {
  return argv.slice(config.IS_PRODUCTION ? 1 : 2)
}

function processArgv (argv) {
  var torrentIds = []
  argv.forEach(function (arg) {
    if (arg === '-n') {
      dialog.openSeedDirectory()
    } else if (arg === '-o') {
      dialog.openTorrentFile()
    } else if (arg === '-u') {
      dialog.openTorrentAddress()
    } else if (arg.startsWith('-psn')) {
      // Ignore Mac launchd "process serial number" argument
      // Issue: https://github.com/feross/webtorrent-desktop/issues/214
    } else {
      torrentIds.push(arg)
    }
  })
  if (torrentIds.length > 0) {
    windows.main.dispatch('onOpen', torrentIds)
  }
}
<|endoftext|>


<commit_before>/**
 * Not much to see here. Just some routes.
 */
import version from './services/version';
import Spatial from './services/spatial';
import Summary from './services/summary';
import logger from './util/logger';

function js(req, res, next) {
  //default encoding is utf-8. null encoding === binary encoding
  req.setEncoding(null);
  if(!req.accepts('json')) {
    req.log.warn("Request does not accept application/json!")
  }
  res.setHeader('content-type', 'application/json');
  next();
}

function router(config, app, zookeeper) {
  var spatial = new Spatial(zookeeper);
  var summary = new Summary();

  app.get('/version', js, app.logger.request(), version.get);
  app.post('/summary', js, app.logger.request(), summary.post.bind(summary));
  app.post('/spatial', js, app.logger.request(), spatial.post.bind(spatial));
}

export default router;<commit_msg>Found it!<commit_after>/**
 * Not much to see here. Just some routes.
 */
import version from './services/version';
import Spatial from './services/spatial';
import Summary from './services/summary';
import logger from './util/logger';

function js(req, res, next) {
  if(!req.accepts('json')) {
    req.log.warn("Request does not accept application/json!")
  }
  res.setHeader('content-type', 'application/json');
  next();
}

function router(config, app, zookeeper) {
  var spatial = new Spatial(zookeeper);
  var summary = new Summary();

  app.get('/version', js, app.logger.request(), version.get);
  app.post('/summary', js, app.logger.request(), summary.post.bind(summary));
  app.post('/spatial', js, app.logger.request(), spatial.post.bind(spatial));
}

export default router;<|endoftext|>


<commit_before>Router.configure({
    layoutTemplate: 'layout',
    loadingTemplate: 'loading',
    notFoundTemplate: 'notFound',
    waitOn: function() {
        Meteor.subscribe('favorites')
    }
});
BookmarksListController = RouteController.extend({
    template: 'bookmarksList',
    increment: 50,
    limit: function() {
        return parseInt(this.params.bookmarksLimit) || this.increment;
    },
    findOptions: function() {
        if (Meteor.isClient) {
            if (Session.get("sortName") && Session.get("sortValue")) {
                var name = Session.get("sortName");
                var value = Session.get("sortValue");
                var sort = {};
                sort[name] = value;
                return {
                    sort: sort,
                    limit: this.limit()
                }
            } else {
                return {
                    sort: {
                        dateCreated: -1
                    },
                    limit: this.limit()
                }
            }
        } else {
            return {
                sort: {
                    dateCreated: -1
                },
                limit: this.limit()
            }
        }
    },
    subscriptions: function() {
        var filter;
        if (Meteor.isClient) {
            filter = this.params.query.filter;
            Session.set('searchBar', filter);
        }
        this.bookmarksSub = Meteor.subscribe('bookmarks', this.findOptions(), filter);
    },
    bookmarks: function() {
        return Bookmarks.find({}, this.findOptions());
    },
    data: function() {
        var hasMore = this.bookmarks().fetch().length === this.limit();
        var nextPath = this.route.path({
            bookmarksLimit: this.limit() + this.increment
        });
        return {
            bookmarks: this.bookmarks(),
            ready: this.bookmarksSub.ready,
            nextPath: hasMore ? nextPath : null
        };
    }
});
Router.route('/about', function() {
    this.render('about');
});
Router.route('/submit', {
    name: 'bookmarkSubmit'
});
Router.route('/export', function(id) {
    var userId = id.cookies.meteor_userid;
    var filename = 'filename.json';
    var allBookmarks = Bookmarks.find({
        userId: userId
    }).fetch();
    var allFavorites = Favorites.find({
        userId: userId
    }).fetch();
    var data = []
    var dataFavorite = []
    _.each(allBookmarks, function(bookmark) {
        data.push(_.omit(bookmark, '_id', 'userId'));
    });
    _.each(allFavorites, function(favorite) {
        dataFavorite.push(_.omit(favorite, '_id', 'userId'));
    });
    var headers = {
        'Content-type': 'application/json',
        'Content-Disposition': "attachment; filename=" + filename
    };
    this.response.writeHead(200, headers);
    return this.response.end("{ \"bookmarks\": " + EJSON.stringify(data) + ", \"favorites\": " + EJSON.stringify(dataFavorite) + "}");
}, {
    where: 'server'
});
Router.route('bookmarkSetting');
Router.route('/bookmark/:_id', {
    name: 'bookmarkPage',
    waitOn: function() {
        return Meteor.subscribe('bookmark', this.params._id);
    },
    data: function() {
        return Bookmarks.findOne(this.params._id);
    }
});
Router.route('bookmarkView', {
    path: '/bookmark/view/:_id',
    waitOn: function() {
        return Meteor.subscribe('bookmark', this.params._id);
    },
    data: function() {
        return Bookmarks.findOne(this.params._id);
    }
});
Router.map(function() {
    this.route('bookmarksList', {
        path: '/home/:bookmarksLimit?',
        controller: BookmarksListController,
        fastRender: true
    });
});
Router.map(function() {
    this.route('landing', {
        path: '/'
    });
});
Router.route('register');
Router.route('passwordRecovery');
Router.route('login');
var mustBeSignedIn = function() {
    if (!Meteor.user()) {
        if (Meteor.loggingIn()) {
            this.render(this.loadingTemplate);
        } else {
            Router.go('/home');
        }
    } else {
        this.next();
    }
};
Router.onBeforeAction('dataNotFound', {
    only: 'bookmarkPage'
});
Router.onBeforeAction(mustBeSignedIn, {
    except: ['landing', 'login', 'register']
});
<commit_msg>fixed code indentation<commit_after>Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() {
    Meteor.subscribe('favorites')
  }
});
BookmarksListController = RouteController.extend({
  template: 'bookmarksList',
  increment: 50,
  limit: function() {
    return parseInt(this.params.bookmarksLimit) || this.increment;
  },
  findOptions: function() {
    if (Meteor.isClient) {
      if (Session.get("sortName") && Session.get("sortValue")) {
        var name = Session.get("sortName");
        var value = Session.get("sortValue");
        var sort = {};
        sort[name] = value;
        return {
          sort: sort,
          limit: this.limit()
        }
      } else {
        return {
          sort: {
            dateCreated: -1
          },
          limit: this.limit()
        }
      }
    } else {
      return {
        sort: {
          dateCreated: -1
        },
        limit: this.limit()
      }
    }
  },
  subscriptions: function() {
    var filter;
    if (Meteor.isClient) {
      filter = this.params.query.filter;
      Session.set('searchBar', filter);
    }
    this.bookmarksSub = Meteor.subscribe('bookmarks', this.findOptions(), filter);
  },
  bookmarks: function() {
    return Bookmarks.find({}, this.findOptions());
  },
  data: function() {
    var hasMore = this.bookmarks().fetch().length === this.limit();
    var nextPath = this.route.path({
      bookmarksLimit: this.limit() + this.increment
    });
    return {
      bookmarks: this.bookmarks(),
      ready: this.bookmarksSub.ready,
      nextPath: hasMore ? nextPath : null
    };
  }
});
Router.route('/about', function() {
  this.render('about');
});
Router.route('/submit', {
  name: 'bookmarkSubmit'
});
Router.route('/export', function(id) {
  var userId = id.cookies.meteor_userid;
  var filename = 'filename.json';
  var allBookmarks = Bookmarks.find({
    userId: userId
  }).fetch();
  var allFavorites = Favorites.find({
    userId: userId
  }).fetch();
  var data = []
  var dataFavorite = []
  _.each(allBookmarks, function(bookmark) {
    data.push(_.omit(bookmark, '_id', 'userId'));
  });
  _.each(allFavorites, function(favorite) {
    dataFavorite.push(_.omit(favorite, '_id', 'userId'));
  });
  var headers = {
    'Content-type': 'application/json',
    'Content-Disposition': "attachment; filename=" + filename
  };
  this.response.writeHead(200, headers);
  return this.response.end("{ \"bookmarks\": " + EJSON.stringify(data) + ", \"favorites\": " + EJSON.stringify(dataFavorite) + "}");
}, {
  where: 'server'
});
Router.route('bookmarkSetting');
Router.route('/bookmark/:_id', {
  name: 'bookmarkPage',
  waitOn: function() {
    return Meteor.subscribe('bookmark', this.params._id);
  },
  data: function() {
    return Bookmarks.findOne(this.params._id);
  }
});
Router.route('bookmarkView', {
  path: '/bookmark/view/:_id',
  waitOn: function() {
    return Meteor.subscribe('bookmark', this.params._id);
  },
  data: function() {
    return Bookmarks.findOne(this.params._id);
  }
});
Router.map(function() {
  this.route('bookmarksList', {
    path: '/home/:bookmarksLimit?',
    controller: BookmarksListController,
    fastRender: true
  });
});
Router.map(function() {
  this.route('landing', {
    path: '/'
  });
});
Router.route('register');
Router.route('passwordRecovery');
Router.route('login');
var mustBeSignedIn = function() {
  if (!Meteor.user()) {
    if (Meteor.loggingIn()) {
      this.render(this.loadingTemplate);
    } else {
      Router.go('/home');
    }
  } else {
    this.next();
  }
};
Router.onBeforeAction('dataNotFound', {
  only: 'bookmarkPage'
});
Router.onBeforeAction(mustBeSignedIn, {
  except: ['landing', 'login', 'register']
});<|endoftext|>


<commit_before>/**
 * This was from a proposal that James Edwards / Brothercake had during
 * 2011 Highland Fling - to use select elements as a starting point for
 * the range element - so it's a pretty darn good fit, I put this together
 * during his Q&A. In the end I needed to lift the detection code from
 * Modernizr - which credit really goes to @miketaylr.
 * My code starts from "if (bool) {"
 *
 * This code is looking for <select type="range"> and will progressively
 * enhance to a input[type=range] copying much of the attributes across.
 */
!function () {
var rangeTest = document.createElement('input'),
    smile = ':)';
rangeTest.setAttribute('type', 'range');

var bool = rangeTest.type !== 'text';
if (bool) {
  rangeTest.style.cssText = 'position:absolute;visibility:hidden;';
  rangeTest.value = smile;
  if (rangeTest.style.WebkitAppearance !== undefined ) {

    document.body.appendChild(rangeTest);
    defaultView = document.defaultView;

    // Safari 2-4 allows the smiley as a value, despite making a slider
    bool =  defaultView.getComputedStyle &&
            defaultView.getComputedStyle(rangeTest, null).WebkitAppearance !== 'textfield' &&
            // Mobile android web browser has false positive, so must
            // check the height to see if the widget is actually there.
            (rangeTest.offsetHeight !== 0);

    document.body.removeChild(rangeTest);
  }
} else {
  bool = rangeTest.value == smile;
}

// if the input[range] is natively supported, then upgrade the <select type="range">
// into a range element.
if (bool) {
 function firstChild(el, nodeName) {
    nodeName = nodeName.toUpperCase();
    if (el.firstChild.nodeName === nodeName) {
      return el.firstChild;
    } else {
      return el.firstChild.nextSibling;
    }
  }

  function lastChild(el, nodeName) {
    nodeName = nodeName.toUpperCase();
    if (el.lastChild.nodeName === nodeName) {
      return el.lastChild;
    } else {
      return el.lastChild.previousSibling;
    }
  }

 var selects = document.getElementsByTagName('select'),
     i = 0;

  for (; i < selects.length; i++) {
    if (selects[i].getAttribute('data-type') == 'range') (function (select) {
      var range = document.createElement('input'),
          parent = select.parentNode;

      range.setAttribute('type', 'range');
      // works with the select element removed from the DOM
      select = parent.replaceChild(range, select);
      range.autofocus = select.autofocus;
      range.disabled = select.disabled;
      range.autocomplete = select.autocomplete; // eh? how would this even work?
      range.className = select.className;
      range.id = select.id;
      range.style = select.style;
      range.tabindex = select.tabindex;
      range.title = select.title;
      range.min = firstChild(select, 'option').value;
      range.max = lastChild(select, 'option').value;
      range.value = select.value;
      // Add step support
      if ( select.getAttribute('data-type-range-step') ) {
        range.step = select.getAttribute('data-type-range-step');
      }
      // yeah, this is filth, but it's because getElementsByTagName is
      // a live DOM collection, so when we removed the select element
      // the selects object reduced in length. Freaky, eh?
      i--;
    })(selects[i]);
  }
}
}();<commit_msg>copying selects name attribute to range input<commit_after>/**
 * This was from a proposal that James Edwards / Brothercake had during
 * 2011 Highland Fling - to use select elements as a starting point for
 * the range element - so it's a pretty darn good fit, I put this together
 * during his Q&A. In the end I needed to lift the detection code from
 * Modernizr - which credit really goes to @miketaylr.
 * My code starts from "if (bool) {"
 *
 * This code is looking for <select type="range"> and will progressively
 * enhance to a input[type=range] copying much of the attributes across.
 */
!function () {
var rangeTest = document.createElement('input'),
    smile = ':)';
rangeTest.setAttribute('type', 'range');

var bool = rangeTest.type !== 'text';
if (bool) {
  rangeTest.style.cssText = 'position:absolute;visibility:hidden;';
  rangeTest.value = smile;
  if (rangeTest.style.WebkitAppearance !== undefined ) {

    document.body.appendChild(rangeTest);
    defaultView = document.defaultView;

    // Safari 2-4 allows the smiley as a value, despite making a slider
    bool =  defaultView.getComputedStyle &&
            defaultView.getComputedStyle(rangeTest, null).WebkitAppearance !== 'textfield' &&
            // Mobile android web browser has false positive, so must
            // check the height to see if the widget is actually there.
            (rangeTest.offsetHeight !== 0);

    document.body.removeChild(rangeTest);
  }
} else {
  bool = rangeTest.value == smile;
}

// if the input[range] is natively supported, then upgrade the <select type="range">
// into a range element.
if (bool) {
 function firstChild(el, nodeName) {
    nodeName = nodeName.toUpperCase();
    if (el.firstChild.nodeName === nodeName) {
      return el.firstChild;
    } else {
      return el.firstChild.nextSibling;
    }
  }

  function lastChild(el, nodeName) {
    nodeName = nodeName.toUpperCase();
    if (el.lastChild.nodeName === nodeName) {
      return el.lastChild;
    } else {
      return el.lastChild.previousSibling;
    }
  }

 var selects = document.getElementsByTagName('select'),
     i = 0;

  for (; i < selects.length; i++) {
    if (selects[i].getAttribute('data-type') == 'range') (function (select) {
      var range = document.createElement('input'),
          parent = select.parentNode;

      range.setAttribute('type', 'range');
      // works with the select element removed from the DOM
      select = parent.replaceChild(range, select);
      range.autofocus = select.autofocus;
      range.disabled = select.disabled;
      range.autocomplete = select.autocomplete; // eh? how would this even work?
      range.className = select.className;
      range.id = select.id;
      range.style = select.style;
      range.tabindex = select.tabindex;
      range.title = select.title;
      range.min = firstChild(select, 'option').value;
      range.max = lastChild(select, 'option').value;
      range.value = select.value;
      range.name = select.name;
      // Add step support
      if ( select.getAttribute('data-type-range-step') ) {
        range.step = select.getAttribute('data-type-range-step');
      }
      // yeah, this is filth, but it's because getElementsByTagName is
      // a live DOM collection, so when we removed the select element
      // the selects object reduced in length. Freaky, eh?
      i--;
    })(selects[i]);
  }
}
}();
<|endoftext|>


<commit_before>module.exports = function (app) {

  const express = require('express');
  const router = express.Router();

  /**
   * routes
   */
  const NoteController = require('./note/NoteController')(app);
  router.use('/notes', NoteController);

  const AuthController = require('./auth/AuthController')(app);
  router.use('/auth', AuthController);

  const UserController = require('./user/UserController')(app);
  router.use('/users', UserController);

  /**
   * default error handling
   */
  function errorHandler(err, req, res, next) {
    logger.error(err.stack);
    res.status(err.status || 500).send(err.message);
    next();
  }
  router.use(errorHandler);

  /**
   * default root route for api
   */
  app.use('/api', router);
  app.get('/api', (req, res) => res.status(200).send('Api Works.'));

};<commit_msg>(backend) added object res for smoke test<commit_after>module.exports = function (app) {

  const express = require('express');
  const router = express.Router();

  /**
   * routes
   */
  const NoteController = require('./note/NoteController')(app);
  router.use('/notes', NoteController);

  const AuthController = require('./auth/AuthController')(app);
  router.use('/auth', AuthController);

  const UserController = require('./user/UserController')(app);
  router.use('/users', UserController);

  /**
   * default error handling
   */
  function errorHandler(err, req, res, next) {
    logger.error(err.stack);
    res.status(err.status || 500).send(err.message);
    next();
  }
  router.use(errorHandler);

  /**
   * default root route for api
   */
  app.use('/api', router);
  app.get('/api', (req, res) => res.status(200).send({ message: 'Api Works.' }));

};<|endoftext|>


<commit_before>var express  = require('express'),
    handlers = require('./handlers'),
    models   = require('./models'),
    helpers  = require('./helpers'),
    errors   = require('./errors'),
    spike    = require('./spike'),
    scripts  = require('../scripts.json');

function tag(label) {
  return function (req, res, next) {
    req[label] = true;
    next();
  };
}

module.exports = function (app) {
  // A sandbox object to contain some specific objects that are commonly used by
  // handlers. In future it would be ideal that each handler only receives the
  // objects that it requires.
  var sandbox = {
    store:   app.store,
    models:  models,
    mailer:  app.mailer,
    helpers: helpers.createHelpers(app)
  }, binHandler, sessionHandler, errorHandler;

  // Create handlers for accepting incoming requests.
  binHandler = new handlers.BinHandler(sandbox);
  sessionHandler = new handlers.SessionHandler(sandbox);
  errorHandler = new handlers.ErrorHandler(sandbox);

  // Special redirects (to external sources)
  app.get(['/video', '/videos', '/tutorials'], function (req, res) {
    res.redirect('http://www.youtube.com/playlist?list=PLXmT1r4krsTooRDWOrIu23P3SEZ3luIUq');
  });

  app.get('/about', function (req, res) {
    res.redirect('https://github.com/remy/jsbin/blob/master/README.markdown');
  });

  app.get(['/issues', '/bugs'], function (req, res) {
    res.redirect('https://github.com/remy/jsbin/issues/');
  });

  // Handler Events

  binHandler.on('updated', spike.ping.bind(spike));
  binHandler.on('reload', spike.reload.bind(spike));
  binHandler.on('latest-for-user', spike.updateUserSpikes.bind(spike));
  binHandler.on('new-revision', spike.bumpRevision.bind(spike));

  binHandler.on('render-scripts', spike.appendScripts.bind(spike, app.settings.version));

  // Load the bin from the store when encountered in the url. Also handles the
  // "latest" url action.
  app.param('bin', binHandler.loadBin);
  app.param('rev', binHandler.loadBinRevision);
  app.param('name', sessionHandler.loadUser);

  // Set up the routes.
  app.get('/', binHandler.getDefault);
  app.get('/gist/*', binHandler.getDefault);
  app.post('/', binHandler.getFromPost);

  // Runner
  app.get('/runner', function (req, res) {
    res.render('runner', {
      scripts: app.get('is_production') ? false : scripts.runner,
      static: sandbox.helpers.urlForStatic()
    });
  });

  app.get('/api/:bin/:rev?', binHandler.apiGetBin);
  app.post('/api/save', binHandler.apiCreateBin);
  app.post('/api/:bin/save', binHandler.apiCreateRevision);

  // Login/Create account.
  app.post('/sethome', sessionHandler.routeSetHome);
  app.post('/logout', sessionHandler.logoutUser);
  app.post('/forgot', sessionHandler.forgotPassword);
  app.get('/forgot', sessionHandler.requestToken);
  app.get('/reset', sessionHandler.resetPassword);

  // GitHub auth
  app.get('/auth/github', sessionHandler.github);
  app.get('/auth/github/callback', sessionHandler.githubPassportCallback, sessionHandler.githubCallback);

  // List
  app.get('/list/:user', binHandler.getUserBins);
  app.get('/list',       binHandler.getUserBins);
  app.get('/show/:user', binHandler.getUserBins);
  app.get('/user/:user', binHandler.getUserBins);

  // Latest
  app.get('/:bin/latest((.|\/):format)?', binHandler.redirectToLatest);
  app.get('/:bin/latest/edit', binHandler.redirectToLatest);

  // Quick and easy urls for test - allows me to do /rem/last on my mobile devices
  app.get('/:name/last(\-:n)?/:quiet(quiet)?', tag('keepLatest'), binHandler.getLatestForUser, spike.getStream, binHandler.getBinPreview);
  app.get('/:name/last(\-:n)?/edit', binHandler.getLatestForUser, binHandler.getBin);
  app.get('/:name/last(\-:n)?/watch', binHandler.getLatestForUser, binHandler.live, binHandler.getBin);

  // Edit
  app.get('/:bin/:rev?/edit', binHandler.getBin);
  app.get('/:bin/:rev?/watch', tag('live'), binHandler.getBin);
  app.get('/:bin/:rev?/embed', tag('embed'), binHandler.getBin);

  app.post('/:bin/:rev?/report', binHandler.report);

  // Save
  app.post('/save', binHandler.createBin);

  // Use this handler to check for a user creating/claiming their own bin url.
  // We use :url here to prevent loadBin() being called and returning a not
  // found error.
  app.post('/:url/save', binHandler.claimBin);

  // If the above route fails then it's either a clone or a revision. Which
  // the handler can check in the post body.
  app.post('/:bin/:rev?/save', binHandler.createRevisionOrClone);
  app.post('/:bin/:rev?/reload', binHandler.reload);

  // Archive
  app.post('/:bin/:rev/archive', binHandler.archiveBin.bind(null, true));
  // Unarchive
  app.post('/:bin/:rev/unarchive', binHandler.archiveBin.bind(null, false));

  // Download
  app.get('/:bin/:rev?/download', binHandler.downloadBin);

  // Source
  app.get('/:bin/:rev?/source', binHandler.getBinSource);
  app.get('/:bin/:rev?.:format(js|json|css|html|md|markdown|stylus|less|coffee|jade)', binHandler.getBinSourceFile);
  app.get('/:bin/:rev?/:format(js)', function (req, res) {
    // Redirect legacy /js suffix to the new .js extension.
    res.redirect(301, req.path.replace(/\/js$/, '.js'));
  });

  // Log
  app.get('/:bin/:rev/log', spike.getLog);
  app.post('/:bin/:rev/log', spike.postLog);

  // Preview
  app.get('/:bin/:quiet(quiet)?', spike.getStream, binHandler.getBinPreview);
  app.get('/:bin/:rev?/:quiet(quiet)?', spike.getStream, binHandler.getBinPreview);

  // Handle failed auth requests.
  app.use(sessionHandler.githubCallbackWithError);

  // Catch all
  app.use(errorHandler.notFound);

  // Error handler.
  app.use(errorHandler.httpError);

  // Final connect error handler when in development.
  app.configure('development', function () {
    app.use(express.errorHandler({showStack: true, dumpExceptions: true}));
  });

  // Final connect error handler when in development.
  app.configure('production', function () {
    app.use(errorHandler.uncaughtError);
  });
};
<commit_msg>Adding privateCheck as app.param for 'bin'<commit_after>var express  = require('express'),
    handlers = require('./handlers'),
    models   = require('./models'),
    helpers  = require('./helpers'),
    errors   = require('./errors'),
    spike    = require('./spike'),
    scripts  = require('../scripts.json');

function tag(label) {
  return function (req, res, next) {
    req[label] = true;
    next();
  };
}

module.exports = function (app) {
  // A sandbox object to contain some specific objects that are commonly used by
  // handlers. In future it would be ideal that each handler only receives the
  // objects that it requires.
  var sandbox = {
    store:   app.store,
    models:  models,
    mailer:  app.mailer,
    helpers: helpers.createHelpers(app)
  }, binHandler, sessionHandler, errorHandler;

  // Create handlers for accepting incoming requests.
  binHandler = new handlers.BinHandler(sandbox);
  sessionHandler = new handlers.SessionHandler(sandbox);
  errorHandler = new handlers.ErrorHandler(sandbox);

  // Special redirects (to external sources)
  app.get(['/video', '/videos', '/tutorials'], function (req, res) {
    res.redirect('http://www.youtube.com/playlist?list=PLXmT1r4krsTooRDWOrIu23P3SEZ3luIUq');
  });

  app.get('/about', function (req, res) {
    res.redirect('https://github.com/remy/jsbin/blob/master/README.markdown');
  });

  app.get(['/issues', '/bugs'], function (req, res) {
    res.redirect('https://github.com/remy/jsbin/issues/');
  });

  // Handler Events

  binHandler.on('updated', spike.ping.bind(spike));
  binHandler.on('reload', spike.reload.bind(spike));
  binHandler.on('latest-for-user', spike.updateUserSpikes.bind(spike));
  binHandler.on('new-revision', spike.bumpRevision.bind(spike));

  binHandler.on('render-scripts', spike.appendScripts.bind(spike, app.settings.version));

  // Load the bin from the store when encountered in the url. Also handles the
  // "latest" url action.
  app.param('bin', binHandler.loadBin);
  app.param('bin', binHandler.checkPrivate);
  app.param('rev', binHandler.loadBinRevision);
  app.param('name', sessionHandler.loadUser);

  // Set up the routes.
  app.get('/', binHandler.getDefault);
  app.get('/gist/*', binHandler.getDefault);
  app.post('/', binHandler.getFromPost);

  // Runner
  app.get('/runner', function (req, res) {
    res.render('runner', {
      scripts: app.get('is_production') ? false : scripts.runner,
      static: sandbox.helpers.urlForStatic()
    });
  });

  app.get('/api/:bin/:rev?', binHandler.apiGetBin);
  app.post('/api/save', binHandler.apiCreateBin);
  app.post('/api/:bin/save', binHandler.apiCreateRevision);

  // Login/Create account.
  app.post('/sethome', sessionHandler.routeSetHome);
  app.post('/logout', sessionHandler.logoutUser);
  app.post('/forgot', sessionHandler.forgotPassword);
  app.get('/forgot', sessionHandler.requestToken);
  app.get('/reset', sessionHandler.resetPassword);

  // GitHub auth
  app.get('/auth/github', sessionHandler.github);
  app.get('/auth/github/callback', sessionHandler.githubPassportCallback, sessionHandler.githubCallback);

  // List
  app.get('/list/:user', binHandler.getUserBins);
  app.get('/list',       binHandler.getUserBins);
  app.get('/show/:user', binHandler.getUserBins);
  app.get('/user/:user', binHandler.getUserBins);

  // Latest
  app.get('/:bin/latest((.|\/):format)?', binHandler.redirectToLatest);
  app.get('/:bin/latest/edit', binHandler.redirectToLatest);

  // Quick and easy urls for test - allows me to do /rem/last on my mobile devices
  app.get('/:name/last(\-:n)?/:quiet(quiet)?', tag('keepLatest'), binHandler.getLatestForUser, spike.getStream, binHandler.getBinPreview);
  app.get('/:name/last(\-:n)?/edit', binHandler.getLatestForUser, binHandler.getBin);
  app.get('/:name/last(\-:n)?/watch', binHandler.getLatestForUser, binHandler.live, binHandler.getBin);

  // Edit
  app.get('/:bin/:rev?/edit', binHandler.getBin);
  app.get('/:bin/:rev?/watch', tag('live'), binHandler.getBin);
  app.get('/:bin/:rev?/embed', tag('embed'), binHandler.getBin);

  app.post('/:bin/:rev?/report', binHandler.report);

  // Save
  app.post('/save', binHandler.createBin);

  // Use this handler to check for a user creating/claiming their own bin url.
  // We use :url here to prevent loadBin() being called and returning a not
  // found error.
  app.post('/:url/save', binHandler.claimBin);

  // If the above route fails then it's either a clone or a revision. Which
  // the handler can check in the post body.
  app.post('/:bin/:rev?/save', binHandler.createRevisionOrClone);
  app.post('/:bin/:rev?/reload', binHandler.reload);

  // Archive
  app.post('/:bin/:rev/archive', binHandler.archiveBin.bind(null, true));
  // Unarchive
  app.post('/:bin/:rev/unarchive', binHandler.archiveBin.bind(null, false));

  // Download
  app.get('/:bin/:rev?/download', binHandler.downloadBin);

  // Source
  app.get('/:bin/:rev?/source', binHandler.getBinSource);
  app.get('/:bin/:rev?.:format(js|json|css|html|md|markdown|stylus|less|coffee|jade)', binHandler.getBinSourceFile);
  app.get('/:bin/:rev?/:format(js)', function (req, res) {
    // Redirect legacy /js suffix to the new .js extension.
    res.redirect(301, req.path.replace(/\/js$/, '.js'));
  });

  // Log
  app.get('/:bin/:rev/log', spike.getLog);
  app.post('/:bin/:rev/log', spike.postLog);

  // Preview
  app.get('/:bin/:quiet(quiet)?', spike.getStream, binHandler.getBinPreview);
  app.get('/:bin/:rev?/:quiet(quiet)?', spike.getStream, binHandler.getBinPreview);

  // Handle failed auth requests.
  app.use(sessionHandler.githubCallbackWithError);

  // Catch all
  app.use(errorHandler.notFound);

  // Error handler.
  app.use(errorHandler.httpError);

  // Final connect error handler when in development.
  app.configure('development', function () {
    app.use(express.errorHandler({showStack: true, dumpExceptions: true}));
  });

  // Final connect error handler when in development.
  app.configure('production', function () {
    app.use(errorHandler.uncaughtError);
  });
};
<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash-contrib'),
    path = require('path'),
    multiparty = require('connect-multiparty'),
    bodyParser = require('body-parser'),
    statics = require('serve-static'),
    ckeditorPath = require('node-ckeditor'),
    debug = require('debug')('formage');

function registerSockets(registry, cookieMiddleware) { // jshint ignore:line
    var sio = registry.socketio;
    if (!sio) return;
    registry.userSockets = {};

    sio.set('authorization', function (request, callback) {
        debug("Trying to authenticate user by cookie");
        var mockReq = {headers: request.headers};
        var mockRes = {writeHead: _.noop};
        cookieMiddleware(mockReq, mockRes, _.noop);
        request.formageUser = _.walk.pluck(mockReq.session || mockReq.sessionCookies, 'formageUser')[0];
        callback(null, !_.isEmpty(request.formageUser));
    });

    registry.fio = sio.of('/formage');
    registry.fio.on('connection', function (socket) {
        var user = socket.conn.request.formageUser;
        registry.userSockets[user.id] = socket;
        debug("User '%s' connected to socket %s", user.username, socket.id);

        socket.once('disconnect', function () {
            delete registry.userSockets[user.id];
            debug("User '%s' disconnected from socket", user.username);
        });

        _(registry.ref_fields).pluck('ref').unique('modelName').forEach(function (ref) {
            var modelName = ref.modelName;
            debug("socket listening on", modelName);
            socket.on(modelName, function (term, cb) {
                var queryP = (ref.search) ? ref.search(term) : ref.$where('~JSON.stringify(this).search(/' + (_.regexEscape(term) || '.') + '/i);').limit(5).exec();
                queryP.then(
                    function (objs) {
                        debug("socket %s getting %j", socket.id, objs);
                        cb(objs || []);
                    }
                ).end(
                    function (err) {
                        debug(err.stack);
                        cb([]);
                    }
                );
            });
            socket.on('selected', function (data, callback) {
                if (!ref.selected) return callback();
                ref.selected(data).then(
                    function (obj) {
                        var data = (obj && obj._id) ? obj._id.toString() : null;
                        debug("socket %s sending %j", socket.id, data);
                        callback(data);
                    }
                ).end(
                    function (err) {
                        debug("socket CB failed");
                        debug(err.stack);
                        callback();
                    }
                );
            });
        });
    });
}

module.exports = function (rootApp, registry) {
    var app = createExpressSubApp(rootApp, registry);
    rootApp.mountExpress = rootApp.mountExpress || rootApp.use;
    rootApp.mountExpress(registry.root, app);
    registry.app = rootApp.admin_app = app;

    var controllers = require('./controllers')(registry);
    var generalAuth = controllers.authMiddleware();

    app.get('/', generalAuth, controllers.userPanelMiddleware, controllers.routes.index);
    app.get('/login', controllers.routes.login);
    app.post('/login', controllers.routes.postLogin);
    app.get('/logout', controllers.routes.logout);
    app.get('/fakeLogin', controllers.routes.fakeLogin);
    app.get('/model/:modelName', controllers.authMiddleware('view'), controllers.userPanelMiddleware, controllers.routes.model);
    app.get('/model/:modelName/document/:documentId', controllers.authMiddleware('update'), controllers.routes.document);
    app.get('/model/:modelName/inline/:documentId', controllers.authMiddleware('update'), controllers.routes.documentInline);
    // JSON routes
    app.post('/json/model/:modelName/document/:documentId', generalAuth, controllers.json_routes.upsertDocument);
    app.get('/json/model/:modelName/document/:documentId/dependencies', controllers.json_routes.checkDependencies);
    app.post('/json/model/:modelName/order', generalAuth, controllers.json_routes.orderDocuments);
    app.post('/json/model/:modelName/action/:actionId', generalAuth, controllers.json_routes.actionDocuments);

    debug('registered at path %s', app.mountpath || app.route);
    return app;
};

// Voodoo to get Express working with compiled templates
var Templates = require('../generated/templates');
var CompiledEngine = function CompiledEngine(templatePath, locals, callback) {
    var name = path.basename(templatePath, '.jade');
    var ret = Templates[name](locals);
    setImmediate(callback.bind(null, null, ret));
};
// End voodoo

function createExpressSubApp(rootApp, registry) {
    var express = require('express');
    var app = express();
    var viewEngine = CompiledEngine;
    if (debug.enabled) try {
        viewEngine = require('jade').__express;
    } catch (ex) {}
    app.engine('jade', viewEngine);
    app.set('view engine', 'jade');
    app.set('views', path.join(__dirname, '..', 'views'));
    app.locals.pretty = true;
    app.locals.version = registry.version;

    var cookieMiddleware = _(rootApp.stack).pluck('handle').find(function (handle) {
        return ~handle.name.search(/cookie/i);
    });
    if (!cookieMiddleware) {
        cookieMiddleware = require('cookie-session')({keys: [registry.title + '1234!@#$contact'], maxAge: 14 * 24 * 60 * 60});
        app.use(cookieMiddleware);
    }
    registerSockets(registry, cookieMiddleware);

    app.use('/vendor/ckeditor', statics(ckeditorPath));
    app.use(statics(path.join(__dirname, '..', 'assets')));
    app.use(bodyParser.urlencoded({extended: true}));
    app.use(bodyParser.json());
    app.use(multiparty());
    app.use(function (err, req, res, next) {
        debug(err.stack);
        try {
            return res.send(500, err);
        } catch (e) {
            debug('cant send 500');
            debug(e.stack);
            err = e;
        }
        next(err);
    });
    return app;
}
<commit_msg>better session decoding<commit_after>'use strict';
var _ = require('lodash-contrib'),
    path = require('path'),
    multiparty = require('connect-multiparty'),
    bodyParser = require('body-parser'),
    statics = require('serve-static'),
    ckeditorPath = require('node-ckeditor'),
    debug = require('debug')('formage');

function registerSockets(registry, cookieMiddleware) { // jshint ignore:line
    var sio = registry.socketio;
    if (!sio) return;

    sio.set('authorization', function (request, callback) {
        debug("Trying to authenticate user by cookie");
        var mockReq = {headers: request.headers};
        var mockRes = {writeHead: _.noop};
        cookieMiddleware(mockReq, mockRes, _.noop);
        request.formageUser = _.walk.pluck(mockReq, 'formageUser')[0];
        var isAuth = !_.isEmpty(request.formageUser);
        callback(null, isAuth);
    });

    registry.fio = sio.of('/formage');
    registry.fio.on('connection', function (socket) {
        var user = socket.conn.request.formageUser;
        debug("User '%s' connected to socket %s", user.username, socket.id);

        socket.once('disconnect', function () {
            debug("User '%s' disconnected from socket", user.username);
        });

        _(registry.ref_fields).pluck('ref').unique('modelName').forEach(function (ref) {
            var modelName = ref.modelName;
            debug("socket listening on", modelName);
            socket.on(modelName, function (term, cb) {
                var queryP = (ref.search) ? ref.search(term) : ref.$where('~JSON.stringify(this).search(/' + (_.regexEscape(term) || '.') + '/i);').limit(5).exec();
                queryP.then(
                    function (objs) {
                        debug("socket %s getting %j", socket.id, objs);
                        cb(objs || []);
                    }
                ).end(
                    function (err) {
                        debug(err.stack);
                        cb([]);
                    }
                );
            });
            socket.on('selected', function (data, callback) {
                if (!ref.selected) return callback();
                ref.selected(data).then(
                    function (obj) {
                        var data = (obj && obj._id) ? obj._id.toString() : null;
                        debug("socket %s sending %j", socket.id, data);
                        callback(data);
                    }
                ).end(
                    function (err) {
                        debug("socket CB failed");
                        debug(err.stack);
                        callback();
                    }
                );
            });
        });
    });
}

module.exports = function (rootApp, registry) {
    var app = createExpressSubApp(rootApp, registry);
    rootApp.mountExpress = rootApp.mountExpress || rootApp.use;
    rootApp.mountExpress(registry.root, app);
    registry.app = rootApp.admin_app = app;

    var controllers = require('./controllers')(registry);
    var generalAuth = controllers.authMiddleware();

    app.get('/', generalAuth, controllers.userPanelMiddleware, controllers.routes.index);
    app.get('/login', controllers.routes.login);
    app.post('/login', controllers.routes.postLogin);
    app.get('/logout', controllers.routes.logout);
    app.get('/fakeLogin', controllers.routes.fakeLogin);
    app.get('/model/:modelName', controllers.authMiddleware('view'), controllers.userPanelMiddleware, controllers.routes.model);
    app.get('/model/:modelName/document/:documentId', controllers.authMiddleware('update'), controllers.routes.document);
    app.get('/model/:modelName/inline/:documentId', controllers.authMiddleware('update'), controllers.routes.documentInline);
    // JSON routes
    app.post('/json/model/:modelName/document/:documentId', generalAuth, controllers.json_routes.upsertDocument);
    app.get('/json/model/:modelName/document/:documentId/dependencies', controllers.json_routes.checkDependencies);
    app.post('/json/model/:modelName/order', generalAuth, controllers.json_routes.orderDocuments);
    app.post('/json/model/:modelName/action/:actionId', generalAuth, controllers.json_routes.actionDocuments);

    debug('registered at path %s', app.mountpath || app.route);
    return app;
};

// Voodoo to get Express working with compiled templates
var Templates = require('../generated/templates');
var CompiledEngine = function CompiledEngine(templatePath, locals, callback) {
    var name = path.basename(templatePath, '.jade');
    var ret = Templates[name](locals);
    setImmediate(callback.bind(null, null, ret));
};
// End voodoo

function createExpressSubApp(rootApp, registry) {
    var express = require('express');
    var app = express();
    var viewEngine = CompiledEngine;
    if (debug.enabled) try {
        viewEngine = require('jade').__express;
    } catch (ex) {}
    app.engine('jade', viewEngine);
    app.set('view engine', 'jade');
    app.set('views', path.join(__dirname, '..', 'views'));
    app.locals.pretty = true;
    app.locals.version = registry.version;

    var cookieMiddleware = _(rootApp.stack).pluck('handle').find(function (handle) {
        return ~handle.name.search(/cookie/i);
    });
    if (!cookieMiddleware) {
        cookieMiddleware = require('cookie-session')({keys: [registry.title + '1234!@#$contact'], maxAge: 14 * 24 * 60 * 60});
        app.use(cookieMiddleware);
    }
    registerSockets(registry, cookieMiddleware);

    app.use('/vendor/ckeditor', statics(ckeditorPath));
    app.use(statics(path.join(__dirname, '..', 'assets')));
    app.use(bodyParser.urlencoded({extended: true}));
    app.use(bodyParser.json());
    app.use(multiparty());
    app.use(function (err, req, res, next) {
        debug(err.stack);
        try {
            return res.send(500, err);
        } catch (e) {
            debug('cant send 500');
            debug(e.stack);
            err = e;
        }
        next(err);
    });
    return app;
}
<|endoftext|>


<commit_before>// run-time control channel
var agent = require('./agent');
var agentVersion = require('strong-agent/package.json').version;
var async = require('async');
var cluster = require('cluster');
var debug = require('./debug')('runctl');
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var master = require('strong-cluster-control');
var npmls = require('strong-npm-ls');
var os = require('os');
var targetctl = require('./targetctl');
var util = require('util');
var wsChannel = require('strong-control-channel/ws-channel').connect;
var processChannel = require('strong-control-channel/process').attach;

exports.start = start;
exports.onRequest = onRequest; // For testing

// Return the number of event listeners.
var listenerCount = EventEmitter.listenerCount || function(emitter, event) {
  return emitter.listeners(event).length;
};

// sent on startup and with every status message
var osVersion = {
  platform: os.platform(),
  arch: os.arch(),
  release: os.release()
};

var ctlChannel = {
  notify: function notify(req) {
    debug('notify: ipcctl? %j %s', !!ipcctl, debug.json(req));
    if (ipcctl) {
      ipcctl.notify(req);
    }
    server.notify(req);
  },
};
// Expose runctl using local domain server
var server = require('strong-control-channel/server').create(onRequest);

// Expose runctl using node IPC only in the master/supervisor (workers
// attach targetctl to the master).
var ipcctl = null;
if (cluster.isMaster && !process.env.SL_RUN_SKIP_IPCCTL) {
  var wsUrl = process.env.STRONGLOOP_CONTROL;
  if (wsUrl) {
    debug('runctl connect: %s', wsUrl);
    ipcctl = wsChannel(onRequest, wsUrl);
    ipcctl.on('error', function(err) {
      console.error('strong-supervisor: lost connection to control channel: %s',
                    err.message);
      process.exit(1);
    });
    ipcctl.unref();
  } else if (process.send) {
    debug('runctl connect: <process channel>');
    ipcctl = processChannel(onRequest);
  }
}

if (cluster.isMaster) {
  exports.notifyStarted = function() {
    // Must not be done synchronously in the require, we don't know the app name
    // until after run has found the app, and change to its directory.
    ctlChannel.notify({
      wid: 0,
      cmd: 'started',
      pid: process.pid,
      ppid: 0,  // Value is not known for the master process
      pst: master.startTime,
      appName: agent().config.appName,
      agentVersion: agentVersion,
      nodeVersion: process.version,
      osVersion: osVersion,
      setSize: master.size,
    });

    // Store pid for later use in fork messages
    master.pid = process.pid;

    // Status notifications usually come after every fork and exit, but if there
    // are no forks, we still want at least one status notification sent. Note
    // that size may be undefined, which isn't > or < than 1.
    if (!(master.options.size >= 1)) {
      notifyStatus();
    }
  };

  cluster.on('listening', function(worker, address) {
    debug('listening');
    ctlChannel.notify({
      cmd: 'listening',
      wid: worker.id,
      pid: worker.process.pid,
      pst: worker.startTime,
      address: address,
    });
  });

  exports.parentCtl = ctlChannel;

  master.on('fork', function(worker) {
    debug('fork');
    ctlChannel.notify({
      cmd: 'fork',
      wid: worker.id,
      pid: worker.process.pid,
      ppid: master.pid,
      pst: worker.startTime,
    });
    notifyStatus();
  });

  cluster.on('exit', function(worker, code, signal) {
    ctlChannel.notify({
      cmd: 'exit',
      wid: worker.id,
      pid: worker.process.pid,
      pst: worker.startTime,
      suicide: worker.suicide,
      reason: signal || code,
    });
    notifyStatus();
  });

  function notifyStatus() {
    var status = clusterStatus();
    status.cmd = 'status';
    ctlChannel.notify(status);
  }
}

function start(options) {
  var logger = options.logger;

  // XXX(sam) I don't like this 'last one wins' approach, but its impossible to
  // prevent the channel outliving sl-run under all conditions, this is the only
  // robust way I've found.
  try {
    fs.unlinkSync(options.channel);
  } catch (er) {
    /* eslint no-empty:0 */
    // Didn't exist
  }

  server.listen(options.channel);

  server.on('error', function(er) {
    logger.error('control channel failed to listen on `%s`: %s',
                 options.channel, er);
    throw er;
  });

  master.on('stop', function() {
    server.close();
  });
  return server;
}

function onRequest(req, callback) {
  /* eslint no-redeclare:0 */
  debug('request %s', debug.json(req));

  var cmd = req.cmd;
  var rsp = {
  };

  if (cmd === 'status') {
    rsp = clusterStatus();
  } else if (cmd === 'npm-ls') {
    rsp = null;
    npmls.read('.', function(er, data) {
      return callback(er ? {error: er.message} : data);
    });
  } else if (cmd === 'set-size') {
    try {
      if (/cpu/i.test(req.size))
        req.size = os.cpus().length;
      else
        req.size = +req.size;
      master.setSize(req.size);
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'stop') {
    try {
      master.stop();
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'restart') {
    try {
      master.restart();
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'disconnect') {
    cluster.disconnect();

  } else if (cmd === 'fork') {
    rsp = null;
    var worker = cluster.fork();
    // worker emits its own 'fork' _after_ s-c-c has augmented it
    worker.once('fork', function() {
      var rsp = {
        workerID: worker.id,
        processID: worker.process.pid,
        pst: worker.startTime,
      };
      callback(rsp);
    });

  } else if (cmd === 'env-set') {
    for (var k in req.env) {
      process.env[k] = req.env[k];
    }
    rsp = requestAllTargets(req, callback);

  } else if (cmd === 'env-unset') {
    for (var k in req.env) {
      delete process.env[req.env[k]];
    }
    rsp = requestAllTargets(req, callback);

  } else if (cmd === 'tracing') {
    if (req.enabled) {
      process.env.STRONGLOOP_TRACING = 1;
    } else if (process.env.STRONGLOOP_TRACING !== null) {
      delete process.env.STRONGLOOP_TRACING;
    }

    try {
      master.restart();
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'signal') {
    if (req.pid !== process.pid) {
     // Do nothing
    } else if (listenerCount(process, req.signame) > 0) {
      // If there are any listeners for this signal, emit it on the
      // process object.
      process.emit(req.signame);
    } else {
      // If there are no listeners, use the default action.
      process.kill(process.pid, req.signame);
    }

  } else {
    // Pass any others off to the target
    rsp = requestOfTarget(req, rsp, callback);
  }

  if (callback && rsp) {
    process.nextTick(callback.bind(null, rsp));
  }
}

function requestAllTargets(req, callback) {
  console.log('forwarding to all workers:', req);
  var wIds = [];
  for (var w in cluster.workers) {
    wIds.push(cluster.workers[w].id);
  }
  async.map(wIds, forwardToWorker, makeResponse);
  return null;

  function forwardToWorker(wid, next) {
    var wreq = JSON.parse(JSON.stringify(req));
    wreq.target = wid;
    console.log('forwarding %j', wreq);
    requestOfTarget(wreq, {}, function(rsp) {
      next(null, rsp);
    });
  }

  function makeResponse(_, res) {
    callback(res);
  }
}

function requestOfTarget(req, rsp, callback) {
  debug('requestOfTarget %j', req);

  if (targetctl.request(+req.target, req, wrapCallback)) {
    rsp = null;
  } else {
    rsp.error = util.format('target %s not found', req.target);
  }

  return rsp;

  function wrapCallback(rsp) {
    // add pst field in master since workers don't know their own pst
    var target = cluster.workers[+rsp.target] || master;
    rsp.pst = target.startTime;
    if (rsp.notify) {
      ctlChannel.notify(rsp.notify);
      delete rsp.notify;
    }

    callback(rsp);
  }
}

function clusterStatus() {
  var mStatus = master.status();
  // pst is used by PM, startTime is set by strong-cluster-control
  mStatus.master.pst = mStatus.master.pst || mStatus.master.startTime;
  mStatus.appName = agent().config.appName;
  mStatus.agentVersion = agentVersion;
  mStatus.nodeVersion = process.version;
  mStatus.osVersion = osVersion;
  return mStatus;
}
<commit_msg>runctl: fork ppid should not depend on msg order<commit_after>// run-time control channel
var agent = require('./agent');
var agentVersion = require('strong-agent/package.json').version;
var async = require('async');
var cluster = require('cluster');
var debug = require('./debug')('runctl');
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var master = require('strong-cluster-control');
var npmls = require('strong-npm-ls');
var os = require('os');
var targetctl = require('./targetctl');
var util = require('util');
var wsChannel = require('strong-control-channel/ws-channel').connect;
var processChannel = require('strong-control-channel/process').attach;

exports.start = start;
exports.onRequest = onRequest; // For testing

// Return the number of event listeners.
var listenerCount = EventEmitter.listenerCount || function(emitter, event) {
  return emitter.listeners(event).length;
};

// sent on startup and with every status message
var osVersion = {
  platform: os.platform(),
  arch: os.arch(),
  release: os.release()
};

var ctlChannel = {
  notify: function notify(req) {
    debug('notify: ipcctl? %j %s', !!ipcctl, debug.json(req));
    if (ipcctl) {
      ipcctl.notify(req);
    }
    server.notify(req);
  },
};
// Expose runctl using local domain server
var server = require('strong-control-channel/server').create(onRequest);

// Expose runctl using node IPC only in the master/supervisor (workers
// attach targetctl to the master).
var ipcctl = null;
if (cluster.isMaster && !process.env.SL_RUN_SKIP_IPCCTL) {
  var wsUrl = process.env.STRONGLOOP_CONTROL;
  if (wsUrl) {
    debug('runctl connect: %s', wsUrl);
    ipcctl = wsChannel(onRequest, wsUrl);
    ipcctl.on('error', function(err) {
      console.error('strong-supervisor: lost connection to control channel: %s',
                    err.message);
      process.exit(1);
    });
    ipcctl.unref();
  } else if (process.send) {
    debug('runctl connect: <process channel>');
    ipcctl = processChannel(onRequest);
  }
}

if (cluster.isMaster) {
  exports.notifyStarted = function() {
    // Must not be done synchronously in the require, we don't know the app name
    // until after run has found the app, and change to its directory.
    ctlChannel.notify({
      wid: 0,
      cmd: 'started',
      pid: process.pid,
      ppid: 0,  // Value is not known for the master process
      pst: master.startTime,
      appName: agent().config.appName,
      agentVersion: agentVersion,
      nodeVersion: process.version,
      osVersion: osVersion,
      setSize: master.size,
    });

    // Status notifications usually come after every fork and exit, but if there
    // are no forks, we still want at least one status notification sent. Note
    // that size may be undefined, which isn't > or < than 1.
    if (!(master.options.size >= 1)) {
      notifyStatus();
    }
  };

  cluster.on('listening', function(worker, address) {
    debug('listening');
    ctlChannel.notify({
      cmd: 'listening',
      wid: worker.id,
      pid: worker.process.pid,
      pst: worker.startTime,
      address: address,
    });
  });

  exports.parentCtl = ctlChannel;

  master.on('fork', function(worker) {
    debug('fork');
    ctlChannel.notify({
      cmd: 'fork',
      wid: worker.id,
      pid: worker.process.pid,
      ppid: process.pid,
      pst: worker.startTime,
    });
    notifyStatus();
  });

  cluster.on('exit', function(worker, code, signal) {
    ctlChannel.notify({
      cmd: 'exit',
      wid: worker.id,
      pid: worker.process.pid,
      pst: worker.startTime,
      suicide: worker.suicide,
      reason: signal || code,
    });
    notifyStatus();
  });

  function notifyStatus() {
    var status = clusterStatus();
    status.cmd = 'status';
    ctlChannel.notify(status);
  }
}

function start(options) {
  var logger = options.logger;

  // XXX(sam) I don't like this 'last one wins' approach, but its impossible to
  // prevent the channel outliving sl-run under all conditions, this is the only
  // robust way I've found.
  try {
    fs.unlinkSync(options.channel);
  } catch (er) {
    /* eslint no-empty:0 */
    // Didn't exist
  }

  server.listen(options.channel);

  server.on('error', function(er) {
    logger.error('control channel failed to listen on `%s`: %s',
                 options.channel, er);
    throw er;
  });

  master.on('stop', function() {
    server.close();
  });
  return server;
}

function onRequest(req, callback) {
  /* eslint no-redeclare:0 */
  debug('request %s', debug.json(req));

  var cmd = req.cmd;
  var rsp = {
  };

  if (cmd === 'status') {
    rsp = clusterStatus();
  } else if (cmd === 'npm-ls') {
    rsp = null;
    npmls.read('.', function(er, data) {
      return callback(er ? {error: er.message} : data);
    });
  } else if (cmd === 'set-size') {
    try {
      if (/cpu/i.test(req.size))
        req.size = os.cpus().length;
      else
        req.size = +req.size;
      master.setSize(req.size);
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'stop') {
    try {
      master.stop();
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'restart') {
    try {
      master.restart();
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'disconnect') {
    cluster.disconnect();

  } else if (cmd === 'fork') {
    rsp = null;
    var worker = cluster.fork();
    // worker emits its own 'fork' _after_ s-c-c has augmented it
    worker.once('fork', function() {
      var rsp = {
        workerID: worker.id,
        processID: worker.process.pid,
        pst: worker.startTime,
      };
      callback(rsp);
    });

  } else if (cmd === 'env-set') {
    for (var k in req.env) {
      process.env[k] = req.env[k];
    }
    rsp = requestAllTargets(req, callback);

  } else if (cmd === 'env-unset') {
    for (var k in req.env) {
      delete process.env[req.env[k]];
    }
    rsp = requestAllTargets(req, callback);

  } else if (cmd === 'tracing') {
    if (req.enabled) {
      process.env.STRONGLOOP_TRACING = 1;
    } else if (process.env.STRONGLOOP_TRACING !== null) {
      delete process.env.STRONGLOOP_TRACING;
    }

    try {
      master.restart();
    } catch (er) {
      rsp.error = er.message;
    }

  } else if (cmd === 'signal') {
    if (req.pid !== process.pid) {
     // Do nothing
    } else if (listenerCount(process, req.signame) > 0) {
      // If there are any listeners for this signal, emit it on the
      // process object.
      process.emit(req.signame);
    } else {
      // If there are no listeners, use the default action.
      process.kill(process.pid, req.signame);
    }

  } else {
    // Pass any others off to the target
    rsp = requestOfTarget(req, rsp, callback);
  }

  if (callback && rsp) {
    process.nextTick(callback.bind(null, rsp));
  }
}

function requestAllTargets(req, callback) {
  console.log('forwarding to all workers:', req);
  var wIds = [];
  for (var w in cluster.workers) {
    wIds.push(cluster.workers[w].id);
  }
  async.map(wIds, forwardToWorker, makeResponse);
  return null;

  function forwardToWorker(wid, next) {
    var wreq = JSON.parse(JSON.stringify(req));
    wreq.target = wid;
    console.log('forwarding %j', wreq);
    requestOfTarget(wreq, {}, function(rsp) {
      next(null, rsp);
    });
  }

  function makeResponse(_, res) {
    callback(res);
  }
}

function requestOfTarget(req, rsp, callback) {
  debug('requestOfTarget %j', req);

  if (targetctl.request(+req.target, req, wrapCallback)) {
    rsp = null;
  } else {
    rsp.error = util.format('target %s not found', req.target);
  }

  return rsp;

  function wrapCallback(rsp) {
    // add pst field in master since workers don't know their own pst
    var target = cluster.workers[+rsp.target] || master;
    rsp.pst = target.startTime;
    if (rsp.notify) {
      ctlChannel.notify(rsp.notify);
      delete rsp.notify;
    }

    callback(rsp);
  }
}

function clusterStatus() {
  var mStatus = master.status();
  // pst is used by PM, startTime is set by strong-cluster-control
  mStatus.master.pst = mStatus.master.pst || mStatus.master.startTime;
  mStatus.appName = agent().config.appName;
  mStatus.agentVersion = agentVersion;
  mStatus.nodeVersion = process.version;
  mStatus.osVersion = osVersion;
  return mStatus;
}
<|endoftext|>