Пример #1
0
module.exports = function(O){
  //////////////////////////////////////////////////////////////////////////////
  ////                            SETUP                                     ////
  //////////////////////////////////////////////////////////////////////////////

  process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';

  var Opts = O || new Optionall({
                                  '__dirname': Path.resolve(module.filename + '/../..')
                                , 'file_priority': [
                                    'package.json'
                                  , 'environment.json'
                                  , 'credentials.json'
                                  ]
                                });

  var S = new (Events.EventEmitter.bind({}))();
  S.settings = Belt.extend({
    'log_level': 'debug'
  }, Opts);

  var log = Opts.log || new Winston.Logger();
  if (!Opts.log) log.add(Winston.transports.Console, {'level': S.settings.log_level, 'colorize': true, 'timestamp': false});
  S.log = log;

  //error handler
  S.on('error', function(err){
//    Request({
//      'url': S.settings['2post']
//    , 'method': 'post'
//    , 'form': {
//        'event': 'server error'
//      , 'message': Belt.get(err, 'message')
//      }
//    }, Belt.noop);

    log.error(err);
  });

////////////////////////////////////////////////////////////////////////////////
////SERVICES / DATA                                                         ////
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
////SETUP                                                                   ////
////////////////////////////////////////////////////////////////////////////////

  /*
    setup redis
  */
  S['setupRedis'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
    
    });

    var ocb = _.once(a.cb);

    self['redis'] = Redis.createClient(a.o);

    self.redis.on('error', function(err){
      return self.emit('error', err);
    });

    self.redis.on('ready', function(){
      self.log.info('Connected to Redis...');
      return ocb();
    });
  };

  /*
    setup session store
  */
  S['setupSessions'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {

    });

    self['sessionsStore'] = new RedisSessions(a.o);
    self['sessions'] = self.sessionsStore; //alias

    a.cb();
    return self;
  };

  /*
    setup express server for incoming requests
  */
  S['setupServer'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      //session_secret
      'cookie_secret': Crypto.randomBytes(512).toString('utf-8')
    , 'body_parser': {
        'limit': '500mb'
      , 'parameterLimit': 10000
      , 'extended': true
      }
    , 'sessions': {
        'store': self.sessionsStore
      , 'secret': a.o.session_secret || Crypto.randomBytes(512).toString('utf-8')
      , 'cookie': {'maxAge': 60000000}
      , 'key': a.o.session_key
      , 'saveUninitialized': true
      , 'resave': true
      }
    , 'views':  Path.join(self.settings.__dirname, '/lib/views')
    });

    self['express'] = Express();
    self.express.set('env', self.settings.environment);
    self.express.set('port', a.o.port);
    self.express.set('view engine', 'ejs');
    self.express.set('views', a.o.views);

    /*
      middleware
    */
    self['logger'] = self.settings.environment === 'production' 
      ? Morgan('common', {'skip': function(req, res) { return res.statusCode < 400; }})
      : Morgan('dev');
    self.express.use(self.logger);

    self['bodyParserJSON'] = BodyParser.json(a.o.body_parser);
    self.express.use(self.bodyParserJSON);

    self['bodyParserURLEncoded'] = BodyParser.urlencoded(a.o.body_parser);
    self.express.use(self.bodyParserURLEncoded);

    self['cookieParser'] = CookieParser(a.o.cookie_secret);
    self.express.use(self.cookieParser);

    self['sessions'] = Sessions(a.o.sessions);
    self.express.use(self.sessions);

    self['errorHandler'] = ErrorHandler();
    self.express.use(self.errorHandler);

    //self.express.use(ServeFavicon(Path.join(self.settings.__dirname, a.o.favicon)));

    self.express.use(Timeout('100m'));

    self.express.disable('x-powered-by');
    self.express.set('trust proxy', true);

    self['httpServer'] = HTTP.createServer(self.express).listen(a.o.port, function(){
      log.info('[HTTP] Express server started');
      log.info(Belt.stringify({
        'environment': self.settings.environment.toUpperCase()
      , 'port': self.express.get('port')
      }));

      return a.cb();
    });

    return self;
  };

  S['setupHelpers'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
    
    });

    var gb = {};
    Async.waterfall([
      function(cb){
        return CP.exec('mkdir -p "' + Path.join(self.settings.__dirname, '/lib/helpers') + '"', Belt.cw(cb));
      }
    , function(cb){
        self['helpers'] = _.chain(FS.readdirSync(Path.join(self.settings.__dirname, '/lib/helpers')))
                           .filter(function(f){ return f.match(/\.(js|json)$/i); })
                           .value();

        if (!_.any(self.helpers)) return cb();

        self.helpers = _.object(
                         _.map(self.helpers, function(g){ return g.replace(/\.(js|json)$/i, ''); })
                       , _.map(self.helpers, function(g){
                           return require(Path.join(self.settings.__dirname, '/lib/helpers/', g));
                         })
                       );

        return Async.eachSeries(_.keys(self.helpers), function(k, cb2){
          log.info('Creating helper [%s]...', k);
          self.helpers[k] = new self.helpers[k](_.extend(
            {}, self.settings, {'log': S.log, 'name': k, 'instance': self})
          ).once('ready', Belt.cw(cb2));

          self.helpers[k].on('error', function(err){
            return self.emit('error', err);
          });
        }, Belt.cw(cb, 0));
      }
    ], a.cb);

    return self;
  }

  S['setupControllers'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
    
    });

    var gb = {};
    Async.waterfall([
      function(cb){
        return CP.exec('mkdir -p "' + Path.join(self.settings.__dirname, '/lib/controllers') + '"', Belt.cw(cb));
      }
    , function(cb){
        self['controllers'] = _.chain(FS.readdirSync(Path.join(self.settings.__dirname, '/lib/controllers')))
                           .filter(function(f){ return f.match(/\.(js|json)$/i); })
                           .value();

        if (!_.any(self.controllers)) return cb();

        self.controllers = _.object(
                         _.map(self.controllers, function(g){ return g.replace(/\.(js|json)$/i, ''); })
                       , _.map(self.controllers, function(g){
                           return require(Path.join(self.settings.__dirname, '/lib/controllers/', g));
                         })
                       );

        return Async.eachSeries(_.keys(self.controllers), function(k, cb2){
          log.info('Creating controller [%s]...', k);
          self.controllers[k] = new self.controllers[k](_.extend(
            {}, self.settings, {'log': S.log, 'name': k, 'instance': self})
          ).once('ready', Belt.cw(cb2));

          self.controllers[k].on('error', function(err){
            return self.emit('error', err);
          });
        }, Belt.cw(cb, 0));
      }
    ], a.cb);

    return self;
  }

  Async.waterfall([
    function(cb){
      return S.setupSessions(S.settings.redis, Belt.cw(cb, 0));
    }
  , function(cb){
      return S.setupRedis(_.omit(S.settings.redis, ['prefix']), Belt.cw(cb, 0));
    }
  , function(cb){
      return S.setupServer(S.settings.express, Belt.cw(cb, 0));
    }
  , function(cb){
      S['status'] = {};

      S.express.all('/', function(req, res){
        return res.status(200).json(S.status);
      });

      return cb();
    }
  , function(cb){
      return S.setupHelpers(S.settings, Belt.cw(cb, 0));
    }
  , function(cb){
      return S.setupControllers(S.settings, Belt.cw(cb, 0));
    }
  ], function(err){
    if (err) return S.emit(err);

    log.info('/////READY/////');

    return S.emit('ready');
  });

  return S;
};
Пример #2
0
module.exports = function(O){
  var Opts = O || new Optionall({
                                  '__dirname': Path.resolve(module.filename + '/../..')
                                , 'file_priority': ['package.json', 'environment.json', 'config.json']
                                });

  var S = new (Events.EventEmitter.bind({}))();
  S.settings = Belt.extend({
    'log_level': 'debug'
  // server
  , 'rate_limit': 200
  }, Opts);

  S.instance = S.settings.instance;

  var log = S.instance.log || new Winston.Logger();
  if (!S.instance.log) log.add(Winston.transports.Console, {'level': S.settings.log_level, 'colorize': true, 'timestamp': false});

  S['dbs'] = {};

////////////////////////////////////////////////////////////////////////////////
////METHODS                                                                  ////
////////////////////////////////////////////////////////////////////////////////

  S['dbConnect'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      //db
      'host': self.settings.mongodb.host
    , 'port': self.settings.mongodb.port
    });

    var gb ={};
    return Async.waterfall([
      function(cb){
        gb['db'] = self.dbs[a.o.db];

        if (gb.db) return cb();

        return Mongodb.MongoClient.connect('mongodb://' + a.o.host + ':' + a.o.port + '/' + a.o.db
        , Belt.cs(cb, self.dbs, a.o.db + '.conn', 1, 0));
      }
    , function(cb){
        if (gb.db) return cb();

        gb['db'] = self.dbs[a.o.db];
        gb.db['collections'] = {};

        gb.db.conn.on('error', function(err){
          Belt.get(gb, 'db.conn.close()');
          Belt.delete(self.dbs, a.o.db);
        });

        gb.db.conn.on('close', function(err){
          Belt.get(gb, 'db.conn.close()');
          Belt.delete(self.dbs, a.o.db);
        });

        return cb();
      }
    ], function(err){
      if (!err && !gb.db) err = new Error('db not connected');
      if (err) Belt.delete(self.dbs, a.o.db);

      return a.cb(err, gb.db);
    });
  };

  S['getCollection'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      //db
      //collection
    });

    var gb ={};
    return Async.waterfall([
      function(cb){
        gb['conn'] = Belt.get(self.dbs, a.o.db + '.conn');

        if (gb.conn) return cb();

        return self.dbConnect(a.o, Belt.cs(cb, gb, 'conn', 1, 'conn', 0));
      }
    , function(cb){
        gb['collection'] = Belt.get(self.dbs[a.o.db], 'collections.' + a.o.collection);

        if (gb.collection) return cb();

        return gb.conn.collection(a.o.collection, Belt.cs(cb, gb, 'collection', 1, 0));
      }
    , function(cb){
        if (!gb.collection) return cb(new Error('collection not found'));

        Belt.set(self.dbs[a.o.db], 'collections.' + a.o.collection, gb.collection);

        return cb();
      }
    ], function(err){
      if (err) Belt.delete(self.dbs, a.o.db + '.collections.' + a.o.collection);

      return a.cb(err, gb.collection);
    });
  };

  S['wrapper'] = function(options, callback){
    var a = Belt.argulint(arguments)
      , self = this;
    a.o = _.defaults(a.o, {
      //db
      //collection
      //method
      //args
    });

    var gb ={};
    return Async.waterfall([
      function(cb){
        return self.getCollection(a.o, Belt.cs(cb, gb, 'collection', 1, 0));
      }
    , function(cb){
        if (!gb.collection[a.o.method]) return cb(new Error('method not found'));

        var args;

        if (a.o.method === 'count'){
          args = Belt.objCast(_.pick(a.o.args, [
            'skip'
          , 'limit'
          , 'min'
          , 'max'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          }, {
            'skip_null': true
          });

          return gb.collection.count(
            a.o.args.query || a.o.args.filter
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'find'){
          args = Belt.objCast(_.pick(a.o.args, [
            'skip'
          , 'limit'
          , 'min'
          , 'max'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          }, {
            'skip_null': true
          });

          gb['cursor'] = gb.collection.find(a.o.args.query);
          _.each(args, function(v, k){
            gb.cursor = gb.cursor[k](v);
          });

          return gb.cursor.toArray(Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'findOne'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          }, {
            'skip_null': true
          });

          return gb.collection.findOne(
            a.o.args.query || a.o.args.filter
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'findOneAndUpdate'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          , 'update'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          , 'upsert': 'boolean'
          }, {
            'skip_null': true
          });

          return gb.collection.findOneAndUpdate(
            a.o.args.query || a.o.args.filter
          , a.o.args.update
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'findOneAndDelete'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          }, {
            'skip_null': true
          });

          return gb.collection.findOneAndDelete(
            a.o.args.query || a.o.args.filter
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'findOneAndReplace'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          , 'replacement'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          }, {
            'skip_null': true
          });

          return gb.collection.findOneAndReplace(
            a.o.args.query || a.o.args.filter
          , a.o.args.replacement
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }


        if (a.o.method === 'insertOne'){
          args = Belt.objCast(_.omit(a.o.args, [
            'doc'
          ]), {

          }, {
            'skip_null': true
          });

          return gb.collection.insertOne(
            a.o.args.doc
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'insertMany'){
          args = Belt.objCast(_.omit(a.o.args, [
            'docs'
          ]), {

          }, {
            'skip_null': true
          });

          return gb.collection.insertMany(
            a.o.args.docs
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'deleteOne'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          ]), {

          }, {
            'skip_null': true
          });

          return gb.collection.deleteOne(
            a.o.args.query || a.o.args.filter
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'deleteMany'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          ]), {

          }, {
            'skip_null': true
          });

          return gb.collection.deleteMany(
            a.o.args.query || a.o.args.filter
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'updateOne'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          , 'update'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          , 'upsert': 'boolean'
          }, {
            'skip_null': true
          });

          return gb.collection.updateOne(
            a.o.args.query || a.o.args.filter
          , a.o.args.update
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }

        if (a.o.method === 'updateMany'){
          args = Belt.objCast(_.omit(a.o.args, [
            'query'
          , 'filter'
          , 'update'
          ]), {
            'skip': 'number'
          , 'limit': 'number'
          , 'min': 'number'
          , 'max': 'number'
          , 'upsert': 'boolean'
          }, {
            'skip_null': true
          });

          return gb.collection.updateMany(
            a.o.args.query || a.o.args.filter
          , a.o.args.update
          , args
          , Belt.cs(cb, gb, 'data', 1, 0));
        }
      }
    ], function(err){
      return a.cb(err, gb.data);
    });
  };

  S['rateLimitQueue'] = Async.priorityQueue(function(task, next){
    return task(Belt.cw(next));
  }, S.settings.rate_limit);

////////////////////////////////////////////////////////////////////////////////
////ROUTES                                                                  ////
////////////////////////////////////////////////////////////////////////////////

  S.instance.express.all('/db/:db/collection/:collection/method/:method.json', function (req, res){
    S.rateLimitQueue.push(function(next){
      return S.wrapper({
        'db': req.params.db
      , 'collection': req.params.collection
      , 'method': req.params.method
      , 'args': _.extend({}, req.query || {}, req.body || {})
      }, function(err, data){
        res.status(200).json({
          'error': Belt.get(err, 'message')
        , 'data': data
        });

        next();
      });
    }, new Date().valueOf());
  });

////////////////////////////////////////////////////////////////////////////////
////SETUP                                                                   ////
////////////////////////////////////////////////////////////////////////////////

  setTimeout(function(){
    return S.emit('ready');
  }, 0);

  return S;
};