mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-08 23:15:24 +02:00
- Introduced NoopApiRateLimiter to effectively disable API rate limiting for self-hosted mode. - Updated ApiRateLimiter to delegate to NoopApiRateLimiter when running self-hosted. - Increased Rack::Attack throttle limits significantly for self-hosted deployments. - Added tests for NoopApiRateLimiter to ensure correct behavior. - This allows self-hosted users to make more API requests without restriction, while keeping stricter limits for SaaS deployments.
95 lines
2.3 KiB
Ruby
95 lines
2.3 KiB
Ruby
class ApiRateLimiter
|
|
# Rate limit tiers (requests per hour)
|
|
RATE_LIMITS = {
|
|
standard: 100,
|
|
premium: 1000,
|
|
enterprise: 10000
|
|
}.freeze
|
|
|
|
DEFAULT_TIER = :standard
|
|
|
|
def initialize(api_key)
|
|
@api_key = api_key
|
|
@redis = Redis.new
|
|
end
|
|
|
|
# Check if the API key has exceeded its rate limit
|
|
def rate_limit_exceeded?
|
|
current_count >= rate_limit
|
|
end
|
|
|
|
# Increment the request count for this API key
|
|
def increment_request_count!
|
|
key = redis_key
|
|
current_time = Time.current.to_i
|
|
window_start = (current_time / 3600) * 3600 # Hourly window
|
|
|
|
@redis.multi do |transaction|
|
|
# Use a sliding window with hourly buckets
|
|
transaction.hincrby(key, window_start.to_s, 1)
|
|
transaction.expire(key, 7200) # Keep data for 2 hours to handle sliding window
|
|
end
|
|
end
|
|
|
|
# Get current request count within the current hour
|
|
def current_count
|
|
key = redis_key
|
|
current_time = Time.current.to_i
|
|
window_start = (current_time / 3600) * 3600
|
|
|
|
count = @redis.hget(key, window_start.to_s)
|
|
count.to_i
|
|
end
|
|
|
|
# Get the rate limit for this API key's tier
|
|
def rate_limit
|
|
tier = determine_tier
|
|
RATE_LIMITS[tier]
|
|
end
|
|
|
|
# Calculate seconds until the rate limit resets
|
|
def reset_time
|
|
current_time = Time.current.to_i
|
|
next_window = ((current_time / 3600) + 1) * 3600
|
|
next_window - current_time
|
|
end
|
|
|
|
# Get detailed usage information
|
|
def usage_info
|
|
{
|
|
current_count: current_count,
|
|
rate_limit: rate_limit,
|
|
remaining: [ rate_limit - current_count, 0 ].max,
|
|
reset_time: reset_time,
|
|
tier: determine_tier
|
|
}
|
|
end
|
|
|
|
# Class method to get usage for an API key without incrementing
|
|
def self.usage_for(api_key)
|
|
limit(api_key).usage_info
|
|
end
|
|
|
|
def self.limit(api_key)
|
|
if Rails.application.config.app_mode.self_hosted?
|
|
# Use NoopApiRateLimiter for self-hosted mode
|
|
# This means no rate limiting is applied
|
|
NoopApiRateLimiter.new(api_key)
|
|
else
|
|
new(api_key)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def redis_key
|
|
"api_rate_limit:#{@api_key.id}"
|
|
end
|
|
|
|
def determine_tier
|
|
# For now, all API keys are standard tier
|
|
# This can be extended later to support different tiers based on user subscription
|
|
# or API key configuration
|
|
DEFAULT_TIER
|
|
end
|
|
end
|