create method

Middleware create(
  1. RateLimitConfig config
)

Creates rate limiting middleware.

Implementation

Middleware create(RateLimitConfig config) {
  return (Handler handler) {
    return (Request request) async {
      final ip = _getClientIp(request);

      // Check blacklist
      if (_blacklist.containsKey(ip)) {
        final bannedUntil = _blacklist[ip]!;
        if (DateTime.now().millisecondsSinceEpoch < bannedUntil) {
          Log.w('Blocked request from banned IP: $ip');
          return Response(429, body: 'IP temporarily banned');
        } else {
          _blacklist.remove(ip);
        }
      }

      final now = DateTime.now();
      _requests[ip] ??= [];

      // Clean old requests
      _requests[ip]!.removeWhere(
        (time) => now.difference(time) > config.window,
      );

      // Check rate limit
      if (_requests[ip]!.length >= config.maxRequests) {
        Log.w('Rate limit exceeded for IP: $ip');

        // Ban IP for repeated violations
        final violations = _requests['${ip}_violations'] ??= [];
        violations.add(now);

        if (violations.length >= 5) {
          // Ban for 1 hour
          _blacklist[ip] = now.add(const Duration(hours: 1)).millisecondsSinceEpoch;
          Log.w('IP banned for 1 hour: $ip');
          return Response(429, body: 'IP banned for 1 hour');
        }

        return Response(
          429,
          headers: {
            'Retry-After': '${config.window.inSeconds}',
            'X-RateLimit-Limit': '${config.maxRequests}',
            'X-RateLimit-Remaining': '0',
            'X-RateLimit-Reset': '${now.add(config.window).millisecondsSinceEpoch}',
          },
          body: 'Rate limit exceeded',
        );
      }

      _requests[ip]!.add(now);

      // Add rate limit headers to response
      final response = await handler(request);
      return response.change(headers: {
        ...response.headers,
        'X-RateLimit-Limit': '${config.maxRequests}',
        'X-RateLimit-Remaining': '${config.maxRequests - _requests[ip]!.length}',
      });
    };
  };
}