Просмотр исходного кода

operation check for rate limiter

yuken 3 лет назад
Родитель
Сommit
abdfc2c99a

+ 35 - 0
packages/app/src/server/middlewares/api-rate-limiter.ts

@@ -0,0 +1,35 @@
+import { NextFunction, Request, Response } from 'express';
+
+import loggerFactory from '~/utils/logger';
+
+
+const logger = loggerFactory('growi:middleware:api-rate-limit');
+
+module.exports = (crowi, rateLimiter) => {
+
+  return (req: Request, res: Response, next: NextFunction) => {
+
+    // delete param from url
+    const endpoint = req.url.replace(/\?.*$/, '');
+    const key = req.ip + endpoint;
+
+    rateLimiter.consume(key, 2) // consume 2 points
+      .then((rateLimiterRes) => {
+        // 2 points consumed
+        logger.info(`${key}: consume 2 points!!!`);
+        next();
+      })
+      .catch((rateLimiterRes) => {
+        // Not enough points to consume
+        logger.error(`${key}: point is not enough!`);
+        next();
+      });
+
+
+    // get config from env
+
+    // consume each point
+
+    // return error if point is not enough
+  };
+};

+ 12 - 0
packages/app/src/server/routes/index.js

@@ -18,6 +18,7 @@ import * as userActivation from './user-activation';
 const rateLimit = require('express-rate-limit');
 const rateLimit = require('express-rate-limit');
 const multer = require('multer');
 const multer = require('multer');
 const autoReap = require('multer-autoreap');
 const autoReap = require('multer-autoreap');
+const { RateLimiterMemory } = require('rate-limiter-flexible');
 
 
 const apiLimiter = rateLimit({
 const apiLimiter = rateLimit({
   windowMs: 15 * 60 * 1000, // 15 minutes
   windowMs: 15 * 60 * 1000, // 15 minutes
@@ -26,6 +27,13 @@ const apiLimiter = rateLimit({
     'Too many requests sent from this IP, please try again after 15 minutes',
     'Too many requests sent from this IP, please try again after 15 minutes',
 });
 });
 
 
+const opts = {
+  points: 10, // set default value
+  duration: 100, // set default value
+};
+
+const rateLimiter = new RateLimiterMemory(opts);
+
 autoReap.options.reapOnError = true; // continue reaping the file even if an error occurs
 autoReap.options.reapOnError = true; // continue reaping the file even if an error occurs
 
 
 module.exports = function(crowi, app) {
 module.exports = function(crowi, app) {
@@ -53,6 +61,7 @@ module.exports = function(crowi, app) {
   const search = require('./search')(crowi, app);
   const search = require('./search')(crowi, app);
   const hackmd = require('./hackmd')(crowi, app);
   const hackmd = require('./hackmd')(crowi, app);
   const ogp = require('./ogp')(crowi);
   const ogp = require('./ogp')(crowi);
+  const apiRateLimiter = require('../middlewares/api-rate-limiter')(crowi, rateLimiter);
 
 
   const unavailableWhenMaintenanceMode = generateUnavailableWhenMaintenanceModeMiddleware(crowi);
   const unavailableWhenMaintenanceMode = generateUnavailableWhenMaintenanceModeMiddleware(crowi);
   const unavailableWhenMaintenanceModeForApi = generateUnavailableWhenMaintenanceModeMiddlewareForApi(crowi);
   const unavailableWhenMaintenanceModeForApi = generateUnavailableWhenMaintenanceModeMiddlewareForApi(crowi);
@@ -71,6 +80,9 @@ module.exports = function(crowi, app) {
   // API v3 for auth
   // API v3 for auth
   app.use('/_api/v3', apiV3AuthRouter);
   app.use('/_api/v3', apiV3AuthRouter);
 
 
+  // API rate limiter
+  app.use(apiRateLimiter);
+
   app.get('/'                         , applicationInstalled, unavailableWhenMaintenanceMode, loginRequired, autoReconnectToSearch, injectUserUISettings, page.showTopPage);
   app.get('/'                         , applicationInstalled, unavailableWhenMaintenanceMode, loginRequired, autoReconnectToSearch, injectUserUISettings, page.showTopPage);
 
 
   app.get('/login/error/:reason'      , applicationInstalled, login.error);
   app.get('/login/error/:reason'      , applicationInstalled, login.error);