feat: use a FixedWindowRateLimiter keyed by IP to rate limit emails
we don't talk about the sent_emails table :)
This commit is contained in:
parent
1ce4f9d278
commit
51e335f090
8 changed files with 75 additions and 87 deletions
36
Foxnouns.Backend/Services/EmailRateLimiter.cs
Normal file
36
Foxnouns.Backend/Services/EmailRateLimiter.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Threading.RateLimiting;
|
||||
using NodaTime;
|
||||
using NodaTime.Extensions;
|
||||
|
||||
namespace Foxnouns.Backend.Services;
|
||||
|
||||
public class EmailRateLimiter
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, RateLimiter> _limiters = new();
|
||||
|
||||
private readonly FixedWindowRateLimiterOptions _limiterOptions =
|
||||
new() { Window = TimeSpan.FromHours(2), PermitLimit = 3 };
|
||||
|
||||
private RateLimiter GetLimiter(string bucket) =>
|
||||
_limiters.GetOrAdd(bucket, _ => new FixedWindowRateLimiter(_limiterOptions));
|
||||
|
||||
public bool IsLimited(string bucket, out Duration retryAfter)
|
||||
{
|
||||
RateLimiter limiter = GetLimiter(bucket);
|
||||
RateLimitLease lease = limiter.AttemptAcquire();
|
||||
|
||||
if (!lease.IsAcquired)
|
||||
{
|
||||
retryAfter = lease.TryGetMetadata(MetadataName.RetryAfter, out TimeSpan timeSpan)
|
||||
? timeSpan.ToDuration()
|
||||
: default;
|
||||
}
|
||||
else
|
||||
{
|
||||
retryAfter = Duration.Zero;
|
||||
}
|
||||
|
||||
return !lease.IsAcquired;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue