GitHub Issue: Implement Email Tracking & Rate Limiting System
Background
Currently considering adding rate limiting for email sending. Rather than adding transient rate-limiting data to the User model, we should implement a dedicated email tracking system that can:
- Rate limit any type of email (confirmation, password reset, notifications, etc.)
- Track email sending history
- Be easily extended for future email types
Proposed Architecture
1. Create EmailLog Entity
public class EmailLog
{
public int Id { get; set; }
public string UserId { get; set; } // Foreign key to User
public User User { get; set; }
public string EmailType { get; set; } // "Confirmation", "PasswordReset", etc.
public string RecipientEmail { get; set; }
public DateTime SentAt { get; set; }
public bool WasSuccessful { get; set; }
public string? ErrorMessage { get; set; }
}
2. Add DbSet to AppDbContext
public DbSet<EmailLog> EmailLogs { get; set; }
3. Create IEmailRateLimiter Service
public interface IEmailRateLimiter
{
Task<bool> CanSendEmailAsync(string userId, string emailType, int cooldownMinutes = 1);
Task LogEmailSentAsync(string userId, string emailType, string recipientEmail, bool wasSuccessful, string? errorMessage = null);
}
4. Implement EmailRateLimiter
- Query EmailLogs for user + email type
- Check if last send was within cooldown period
- Return true/false for rate limit check
- Log each email attempt with timestamp and result
5. Integration Points
Update these endpoints to use the rate limiter:
POST /api/auth/resend-confirmation-email
POST /api/auth/forgot-password (future)
- Any other email-sending endpoints
Pattern for each endpoint:
// 1. Check rate limit
if (!await _emailRateLimiter.CanSendEmailAsync(userId, "Confirmation"))
{
return BadRequest(new { errors = new[] { "Please wait before requesting another email" } });
}
// 2. Send email
try
{
await _emailSender.SendEmailAsync(...);
await _emailRateLimiter.LogEmailSentAsync(userId, "Confirmation", email, true);
}
catch (Exception ex)
{
await _emailRateLimiter.LogEmailSentAsync(userId, "Confirmation", email, false, ex.Message);
throw;
}
Benefits
- ✅ Centralized rate limiting logic
- ✅ Historical tracking of all emails sent
- ✅ Easily extensible for new email types
- ✅ Can implement different cooldown periods per email type
- ✅ Debugging capabilities (can see when/why emails failed)
- ✅ User model stays clean (only persistent user data)
Future Enhancements
- Add cleanup job to delete old EmailLog records (e.g., older than 90 days)
- Add admin dashboard to view email sending statistics
- Implement exponential backoff for repeated failures
- Add email type-specific configuration (different cooldowns)
- Track attempt count per time window (e.g., max 3 per hour)
Testing Considerations
- Unit tests for
EmailRateLimiter service with in-memory database
- Test cases:
- First send (should allow)
- Immediate resend (should block)
- Resend after cooldown (should allow)
- Multiple email types don't interfere
- Failed sends are logged correctly
Priority
Medium - Can implement after Phase 2 (real email service) is complete.
Implementation Steps
- Create migration for EmailLog table
- Implement IEmailRateLimiter interface and service
- Register service in DI container
- Update resend-confirmation-email endpoint
- Write unit tests
- Test manually with real email service
- Document in API documentation
Current Workaround
For immediate needs, we can add LastEmailSentAt field to User model as a temporary solution, then refactor to this system when we have multiple email types.
GitHub Issue: Implement Email Tracking & Rate Limiting System
Background
Currently considering adding rate limiting for email sending. Rather than adding transient rate-limiting data to the User model, we should implement a dedicated email tracking system that can:
Proposed Architecture
1. Create EmailLog Entity
2. Add DbSet to AppDbContext
3. Create IEmailRateLimiter Service
4. Implement EmailRateLimiter
5. Integration Points
Update these endpoints to use the rate limiter:
POST /api/auth/resend-confirmation-emailPOST /api/auth/forgot-password(future)Pattern for each endpoint:
Benefits
Future Enhancements
Testing Considerations
EmailRateLimiterservice with in-memory databasePriority
Medium - Can implement after Phase 2 (real email service) is complete.
Implementation Steps
Current Workaround
For immediate needs, we can add
LastEmailSentAtfield to User model as a temporary solution, then refactor to this system when we have multiple email types.