OneConversorTemplate/OnlyOneAccessTemplate/Services/RateLimitService.cs
Ricardo Carneiro 03e6c74ada feat: ratelimit
2025-06-05 11:52:03 -03:00

97 lines
3.1 KiB
C#

using Microsoft.Extensions.Caching.Memory;
using OnlyOneAccessTemplate.Models.RateLimits;
namespace OnlyOneAccessTemplate.Services
{
public class RateLimitService : IRateLimitService
{
private readonly IMemoryCache _cache;
private readonly IConfiguration _configuration;
private const int MAX_REQUESTS_PER_HOUR = 50;
private const int CAPTCHA_THRESHOLD = 20;
public RateLimitService(IMemoryCache cache, IConfiguration configuration)
{
_cache = cache;
_configuration = configuration;
}
public async Task<bool> ShouldShowCaptchaAsync(string ipAddress)
{
var key = $"rate_limit_{ipAddress}";
if (_cache.TryGetValue(key, out RateLimitEntry entry))
{
// Resetar contador se passou mais de 1 hora
if (DateTime.UtcNow.Subtract(entry.LastRequest).TotalHours > 1)
{
entry.RequestCount = 0;
}
return entry.RequestCount >= CAPTCHA_THRESHOLD;
}
return false;
}
public async Task RecordRequestAsync(string ipAddress)
{
var key = $"rate_limit_{ipAddress}";
if (_cache.TryGetValue(key, out RateLimitEntry entry))
{
entry.RequestCount++;
entry.LastRequest = DateTime.UtcNow;
}
else
{
entry = new RateLimitEntry
{
IpAddress = ipAddress,
RequestCount = 1,
LastRequest = DateTime.UtcNow
};
}
_cache.Set(key, entry, TimeSpan.FromHours(2));
}
public async Task<(bool IsValid, string Challenge)> GenerateCaptchaAsync()
{
// Captcha matemático simples
var random = new Random();
var num1 = random.Next(1, 10);
var num2 = random.Next(1, 10);
var operation = random.Next(0, 2) == 0 ? "+" : "-";
var challenge = $"{num1} {operation} {num2}";
var answer = operation == "+" ? num1 + num2 : num1 - num2;
var challengeKey = Guid.NewGuid().ToString();
_cache.Set($"captcha_{challengeKey}", answer, TimeSpan.FromMinutes(10));
return (true, $"{challenge}|{challengeKey}");
}
public async Task<bool> ValidateCaptchaAsync(string challenge, string response)
{
if (string.IsNullOrEmpty(challenge) || string.IsNullOrEmpty(response))
return false;
var parts = challenge.Split('|');
if (parts.Length != 2) return false;
var challengeKey = parts[1];
var cacheKey = $"captcha_{challengeKey}";
if (_cache.TryGetValue(cacheKey, out int expectedAnswer))
{
_cache.Remove(cacheKey);
if (int.TryParse(response, out int userAnswer))
{
return userAnswer == expectedAnswer;
}
}
return false;
}
}
}