main ratelimit.go
Eric Bower  ·  2026-02-25
 1package patchbin
 2
 3import (
 4	"fmt"
 5	"sync"
 6	"time"
 7)
 8
 9// RateLimiter enforces a single global cap on submissions per interval,
10// shared across all users (not keyed by pubkey or IP).
11type RateLimiter struct {
12	mu       sync.Mutex
13	max      int
14	interval time.Duration
15	count    int
16	resetAt  time.Time
17}
18
19func NewRateLimiter(max int, interval time.Duration) *RateLimiter {
20	return &RateLimiter{
21		max:      max,
22		interval: interval,
23	}
24}
25
26// Allow reports whether a new submission is permitted under the current
27// window, incrementing the window's counter if so.
28func (r *RateLimiter) Allow() bool {
29	r.mu.Lock()
30	defer r.mu.Unlock()
31
32	now := time.Now()
33	if now.After(r.resetAt) {
34		r.count = 0
35		r.resetAt = now.Add(r.interval)
36	}
37
38	if r.count >= r.max {
39		return false
40	}
41
42	r.count++
43	return true
44}
45
46func (r *RateLimiter) Error() error {
47	return fmt.Errorf(
48		"rate limit exceeded: max %d submissions per %s, try again later",
49		r.max,
50		r.interval,
51	)
52}