Documentation
¶
Index ¶
- Variables
- type Action
- type Backend
- type EntityType
- type GlobalRateLimitStats
- type HybridNotificationStore
- func (s *HybridNotificationStore) Acknowledge(ctx context.Context, userID int64, traceIDs []string) error
- func (s *HybridNotificationStore) Add(ctx context.Context, userID int64, n Notification) error
- func (s *HybridNotificationStore) GetUnacked(ctx context.Context, userID int64, cursor int64, limit int32) ([]Notification, error)
- func (s *HybridNotificationStore) Subscribe(ctx context.Context, userID int64) (<-chan Notification, func(), error)
- type Notification
- type NotificationStore
- type RateLimitResult
- type RateLimiter
- type RedisBackend
- func (r *RedisBackend) Allow(ctx context.Context, key string) (RateLimitResult, error)
- func (r *RedisBackend) Close() error
- func (r *RedisBackend) GetAndDelete(ctx context.Context, state string) (StateEntry, error)
- func (r *RedisBackend) Ping(ctx context.Context) error
- func (r *RedisBackend) Set(ctx context.Context, state string, entry StateEntry, ttl time.Duration) error
- type RedisConfig
- type RedisTokenCache
- type StateEntry
- type StateStore
- type TokenCache
- type UserRateLimitStats
- type WhoopRateLimitReason
- type WhoopRateLimitState
- type WhoopRateLimiter
- type WhoopRateLimiterConfig
- type WhoopRedisLimiter
- func (w *WhoopRedisLimiter) CheckAndIncrement(ctx context.Context, userKey string) (*WhoopRateLimitState, error)
- func (w *WhoopRedisLimiter) GetGlobalStats(ctx context.Context) (*GlobalRateLimitStats, error)
- func (w *WhoopRedisLimiter) GetUserStats(ctx context.Context, userKey string) (*UserRateLimitStats, error)
- func (w *WhoopRedisLimiter) UpdateFromHeaders(ctx context.Context, headers http.Header) error
Constants ¶
This section is empty.
Variables ¶
View Source
var ErrNotFound = errors.New("state not found")
Functions ¶
This section is empty.
Types ¶
type Backend ¶
type Backend interface {
RateLimiter
StateStore
Close() error
Ping(ctx context.Context) error
}
type EntityType ¶
type EntityType string
const ( EntityTypeRecovery EntityType = "recovery" EntityTypeWorkout EntityType = "workout" EntityTypeSleep EntityType = "sleep" )
type GlobalRateLimitStats ¶
type HybridNotificationStore ¶
type HybridNotificationStore struct {
// contains filtered or unexported fields
}
func NewHybridNotificationStore ¶
func NewHybridNotificationStore(pool *pgxpool.Pool, redis *redis.Client) *HybridNotificationStore
func (*HybridNotificationStore) Acknowledge ¶
func (*HybridNotificationStore) Add ¶
func (s *HybridNotificationStore) Add(ctx context.Context, userID int64, n Notification) error
func (*HybridNotificationStore) GetUnacked ¶
func (s *HybridNotificationStore) GetUnacked(ctx context.Context, userID int64, cursor int64, limit int32) ([]Notification, error)
func (*HybridNotificationStore) Subscribe ¶
func (s *HybridNotificationStore) Subscribe(ctx context.Context, userID int64) (<-chan Notification, func(), error)
type Notification ¶
type NotificationStore ¶
type NotificationStore interface {
// Add persists a notification and publishes to real-time subscribers.
// Idempotent: duplicate trace_ids are ignored.
Add(ctx context.Context, userID int64, n Notification) error
// GetUnacked returns unacknowledged notifications using cursor-based pagination.
// Pass 0 for the first page. Returns notifications with id > cursor.
GetUnacked(ctx context.Context, userID int64, cursor int64, limit int32) ([]Notification, error)
// Acknowledge marks the specified notifications as acknowledged by trace_id.
Acknowledge(ctx context.Context, userID int64, traceIDs []string) error
// Subscribe returns a channel that receives notifications for a user.
// The returned function should be called to unsubscribe.
Subscribe(ctx context.Context, userID int64) (<-chan Notification, func(), error)
}
type RateLimitResult ¶
type RateLimiter ¶
type RateLimiter interface {
Allow(ctx context.Context, key string) (RateLimitResult, error)
}
type RedisBackend ¶
type RedisBackend struct {
// contains filtered or unexported fields
}
func NewRedisBackend ¶
func NewRedisBackend(cfg RedisConfig, rateLimit int) (*RedisBackend, error)
func (*RedisBackend) Allow ¶
func (r *RedisBackend) Allow(ctx context.Context, key string) (RateLimitResult, error)
func (*RedisBackend) Close ¶
func (r *RedisBackend) Close() error
func (*RedisBackend) GetAndDelete ¶
func (r *RedisBackend) GetAndDelete(ctx context.Context, state string) (StateEntry, error)
func (*RedisBackend) Set ¶
func (r *RedisBackend) Set(ctx context.Context, state string, entry StateEntry, ttl time.Duration) error
type RedisConfig ¶
type RedisTokenCache ¶
type RedisTokenCache struct {
// contains filtered or unexported fields
}
func NewRedisTokenCache ¶
func NewRedisTokenCache(cfg RedisConfig) *RedisTokenCache
type StateEntry ¶
type StateStore ¶
type StateStore interface {
Set(ctx context.Context, state string, entry StateEntry, ttl time.Duration) error
// GetAndDelete atomically retrieves and removes a state entry.
// Returns ErrNotFound if the state does not exist or has expired.
GetAndDelete(ctx context.Context, state string) (StateEntry, error)
}
type TokenCache ¶
type TokenCache interface {
// GetUserID returns the cached user ID for a token hash.
// Returns ErrNotFound if not cached or expired.
GetUserID(ctx context.Context, tokenHash string) (int64, error)
// SetUserID caches a user ID for a token hash with TTL.
SetUserID(ctx context.Context, tokenHash string, userID int64, ttl time.Duration) error
}
TokenCache caches validated bearer tokens to avoid repeated API calls. Maps token hash -> WHOOP user ID.
type UserRateLimitStats ¶
type WhoopRateLimitReason ¶
type WhoopRateLimitReason string
const ( WhoopRateLimitReasonPerUserMinute WhoopRateLimitReason = "per-user-minute" WhoopRateLimitReasonPerUserDay WhoopRateLimitReason = "per-user-day" WhoopRateLimitReasonGlobalMinute WhoopRateLimitReason = "global-minute" WhoopRateLimitReasonGlobalDay WhoopRateLimitReason = "global-day" )
type WhoopRateLimitState ¶
type WhoopRateLimiter ¶
type WhoopRateLimiter interface {
// CheckAndIncrement checks both per-user and global limits, incrementing counters if allowed.
// userKey: hash of access token or user ID extracted from token
// Returns combined state - allowed only if BOTH per-user AND global limits pass
CheckAndIncrement(ctx context.Context, userKey string) (*WhoopRateLimitState, error)
// UpdateFromHeaders updates global rate limit state from WHOOP API response headers.
// Syncs global counters with WHOOP's actual values.
UpdateFromHeaders(ctx context.Context, headers http.Header) error
GetUserStats(ctx context.Context, userKey string) (*UserRateLimitStats, error)
GetGlobalStats(ctx context.Context) (*GlobalRateLimitStats, error)
}
type WhoopRateLimiterConfig ¶
type WhoopRedisLimiter ¶
type WhoopRedisLimiter struct {
// contains filtered or unexported fields
}
func NewWhoopRedisLimiter ¶
func NewWhoopRedisLimiter(cfg RedisConfig, config WhoopRateLimiterConfig) *WhoopRedisLimiter
func (*WhoopRedisLimiter) CheckAndIncrement ¶
func (w *WhoopRedisLimiter) CheckAndIncrement(ctx context.Context, userKey string) (*WhoopRateLimitState, error)
func (*WhoopRedisLimiter) GetGlobalStats ¶
func (w *WhoopRedisLimiter) GetGlobalStats(ctx context.Context) (*GlobalRateLimitStats, error)
func (*WhoopRedisLimiter) GetUserStats ¶
func (w *WhoopRedisLimiter) GetUserStats(ctx context.Context, userKey string) (*UserRateLimitStats, error)
func (*WhoopRedisLimiter) UpdateFromHeaders ¶
Click to show internal directories.
Click to hide internal directories.