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 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 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; } } }