package orders import ( "net" "net/http" "strings" "sync" "time" "golang.org/x/time/rate" ) type visitor struct { lim *rate.Limiter seen time.Time } func Limit(next http.Handler, every time.Duration, burst int) http.Handler { var mu sync.Mutex visitors := map[string]*visitor{} go func() { for range time.Tick(10 * time.Minute) { mu.Lock() for ip, v := range visitors { if time.Since(v.seen) > 10*time.Minute { delete(visitors, ip) } } mu.Unlock() } }() return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := clientIP(r) mu.Lock() v, ok := visitors[ip] if !ok { v = &visitor{lim: rate.NewLimiter(rate.Every(every), burst)} visitors[ip] = v } v.seen = time.Now() allowed := v.lim.Allow() mu.Unlock() if !allowed { http.Error(w, "Zbyt wiele prób. Spróbuj ponownie za chwilę.", http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) } func clientIP(r *http.Request) string { host, _, _ := net.SplitHostPort(r.RemoteAddr) if ip := net.ParseIP(host); ip != nil && ip.IsLoopback() { if xff := r.Header.Get("X-Forwarded-For"); xff != "" { return strings.TrimSpace(strings.Split(xff, ",")[0]) } } return host }