rdev/internal/domain/rate_limit.go
jordan 72d16929ca feat: Implement hexagonal architecture with services, webhooks, queue, and telemetry
Major refactoring to hexagonal (ports & adapters) architecture:

- Add service layer (apikey_service, project_service) for business logic
- Add webhook system with dispatcher and delivery tracking
- Add command queue with priority-based processing
- Add rate limiting with sliding window algorithm
- Add audit logging for command execution
- Add OpenTelemetry integration (traces, metrics, spans)
- Add circuit breaker for fault tolerance
- Add cached repository wrapper for performance
- Add comprehensive validation package
- Add Kubernetes client integration for pod management
- Add database migrations (allowed_ips, audit_log, rate_limiting, queue, webhooks)
- Add network policy and PodDisruptionBudget for k8s
- Remove legacy executor and projects/registry packages
- Untrack secrets.yaml (now managed via envault)
- Add coverage.out to .gitignore
- Add e2e test infrastructure with docker-compose
- Add comprehensive documentation (API, architecture, operations, plans)
- Add golangci-lint config and pre-commit hook

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 19:57:46 -07:00

89 lines
2.4 KiB
Go

package domain
import "time"
// Default rate limit values. These must match the defaults in
// internal/db/migrations/005_rate_limiting.sql
const (
DefaultRateLimitPerMinute = 60
DefaultRateLimitPerHour = 1000
)
// RateLimitConfig holds the rate limit configuration for an API key.
type RateLimitConfig struct {
// PerMinute is the maximum number of requests allowed per minute.
PerMinute int
// PerHour is the maximum number of requests allowed per hour.
PerHour int
}
// DefaultRateLimitConfig returns the default rate limit configuration.
func DefaultRateLimitConfig() RateLimitConfig {
return RateLimitConfig{
PerMinute: DefaultRateLimitPerMinute,
PerHour: DefaultRateLimitPerHour,
}
}
// RateLimitState tracks the current usage within a time window.
type RateLimitState struct {
// APIKeyID is the identifier of the API key.
APIKeyID string
// WindowStart is the beginning of the current time window.
WindowStart time.Time
// WindowType indicates the type of window ("minute" or "hour").
WindowType string
// RequestCount is the number of requests made in this window.
RequestCount int
// UpdatedAt is when this state was last updated.
UpdatedAt time.Time
}
// WindowTypeMinute is the constant for minute-based windows.
const WindowTypeMinute = "minute"
// WindowTypeHour is the constant for hour-based windows.
const WindowTypeHour = "hour"
// RateLimitResult contains the result of a rate limit check.
type RateLimitResult struct {
// Allowed indicates whether the request is allowed.
Allowed bool
// RetryAfter is the duration to wait before retrying (when not allowed).
RetryAfter time.Duration
// RemainingMinute is the number of requests remaining in the current minute.
RemainingMinute int
// RemainingHour is the number of requests remaining in the current hour.
RemainingHour int
// LimitMinute is the per-minute limit for this key.
LimitMinute int
// LimitHour is the per-hour limit for this key.
LimitHour int
// ResetMinute is when the minute window resets.
ResetMinute time.Time
// ResetHour is when the hour window resets.
ResetHour time.Time
}
// TruncateToMinute truncates a time to the start of the minute.
func TruncateToMinute(t time.Time) time.Time {
return t.Truncate(time.Minute)
}
// TruncateToHour truncates a time to the start of the hour.
func TruncateToHour(t time.Time) time.Time {
return t.Truncate(time.Hour)
}