express-limiter icon indicating copy to clipboard operation
express-limiter copied to clipboard

Is mongodb supoorted?

Open cookie-ag opened this issue 9 years ago • 4 comments

I tired to change the client to mongo and it failed. Has anyone used it?

Appreciate help. Thanks

cookie-ag avatar Oct 07 '16 10:10 cookie-ag

Modify the db calls in the module and/or submit a PR for a wrapper for calls...

v-kat avatar Oct 07 '16 17:10 v-kat

@IRog I would try to do this by end of the month. I think it would be useful for many people.

cookie-ag avatar Oct 10 '16 05:10 cookie-ag

I have changed db calls to mongo collection queries and that works great for me. If we want this integrated in same package i can submit PR with detailed document and changes. But i am worried about that it would not be backward compatible. I need help to figure out about backward compatibility .

module.exports = function(app, collection) {
  collection.ensureIndex({ 'reset': 1 }, { 'expireAfterSeconds': 0 })
  return function(opts) {
    var middleware = function(req, res, next) {
      if (opts.whitelist && opts.whitelist(req)) return next()
      opts.lookup = Array.isArray(opts.lookup) ? opts.lookup : [opts.lookup]
      opts.onRateLimited = typeof opts.onRateLimited === 'function' ? opts.onRateLimited : function(req, res, next) {
        res.status(429).send({ 'msg': 'Rate limit exceeded' })
      }
      var lookups = opts.lookup.map(function(item) {
        return item + ':' + item.split('.').reduce(function(prev, cur) {
          return prev[cur]
        }, req)
      }).join(':')
      var path = opts.path || req.path
      var method = (opts.method || req.method).toLowerCase()
      var key = 'ratelimit:' + path + ':' + method + ':' + lookups
      collection.findOne({ key: key }, function(err, limit) {
        if (err && opts.ignoreErrors) return next()
        var now = Date.now()
        limit = limit ? limit : {
          total: opts.total,
          remaining: opts.total,
          reset: new Date(now + opts.expire)
        }

        if (now > limit.reset) {
          limit.reset = new Date(now + opts.expire)
          limit.remaining = opts.total
        }

        // do not allow negative remaining
        limit.remaining = Math.max(Number(limit.remaining) - 1, -1)
        collection.update({ key: key }, { $set: { total: limit.total, remaining: limit.remaining, reset: limit.reset } }, { 'upsert': true }, function(e) {
          if (!opts.skipHeaders) {
            res.set('X-RateLimit-Limit', limit.total)
            res.set('X-RateLimit-Reset', Math.ceil(limit.reset / 1000)) // UTC epoch seconds
            res.set('X-RateLimit-Remaining', Math.max(limit.remaining, 0))
          }

          if (limit.remaining >= 0) return next()

          var after = (limit.reset - Date.now()) / 1000
          if (!opts.skipHeaders) res.set('Retry-After', after)

          opts.onRateLimited(req, res, next)
        })

      })
    }
    if (typeof(opts.lookup) === 'function') {
      var callableLookup = opts.lookup;
      middleware = function(middleware, req, res, next) {
        return callableLookup(req, res, opts, function() {
          return middleware(req, res, next)
        })
      }.bind(this, middleware)
    }
    if (opts.method && opts.path) app[opts.method](opts.path, middleware)
    return middleware
  }
};

MrSpark2591 avatar Jul 24 '18 12:07 MrSpark2591

I have created pull request for supporting it. Here is the PR

MrSpark2591 avatar Jul 26 '18 11:07 MrSpark2591