ultisuite-backend/internal/api/mail/sendguard/ratelimit.go
R3D347HR4Y 4eadb91a64 Enhance mail API with rate limiting, idempotency, and attachment management
- Added rate limiting for outbound email sends to prevent abuse, implemented in `internal/api/mail/sendguard`.
- Introduced idempotency key support for email sending to avoid duplicate submissions.
- Enhanced attachment handling with new limits and validation in `internal/api/mail/limits`.
- Updated outbox processing to include retry logic and circuit breaker for SMTP failures.
- Improved HTML sanitization for email content to enhance security.
- Added unit tests for new features, ensuring robust functionality and error handling.
- Updated configuration options in `.env.example` for new mail settings.
2026-05-22 17:19:16 +02:00

56 lines
1009 B
Go

package sendguard
import (
"errors"
"sync"
"time"
"golang.org/x/time/rate"
)
var ErrSendRateLimited = errors.New("send rate limited")
// RateLimiter limits outbound send API requests per user.
type RateLimiter struct {
mu sync.Mutex
limits map[string]*rate.Limiter
limit rate.Limit
burst int
}
func NewRateLimiter(perMinute int, burst int) *RateLimiter {
if perMinute < 1 {
perMinute = 30
}
if burst < 1 {
burst = 10
}
return &RateLimiter{
limits: make(map[string]*rate.Limiter),
limit: rate.Every(time.Minute / time.Duration(perMinute)),
burst: burst,
}
}
func (r *RateLimiter) Allow(userID string) error {
if userID == "" {
return ErrSendRateLimited
}
lim := r.limiter(userID)
if !lim.Allow() {
return ErrSendRateLimited
}
return nil
}
func (r *RateLimiter) limiter(userID string) *rate.Limiter {
r.mu.Lock()
defer r.mu.Unlock()
lim, ok := r.limits[userID]
if !ok {
lim = rate.NewLimiter(r.limit, r.burst)
r.limits[userID] = lim
}
return lim
}