dal-license-server/internal/middleware/ratelimit.go
djuka dc0114e4b7 Inicijalni commit: kompletna implementacija + dokumentacija + testovi
- Kompletna Go implementacija licencnog servera (19 Go fajlova)
- Klijentski API: activate, deactivate, validate
- Admin API: CRUD licence, stats, audit log
- Admin dashboard: htmx + Go templates
- RSA-2048 potpisivanje licencnih podataka
- Rate limiting i API key autentifikacija
- MySQL migracije i seed podaci (ESIR, ARV, LIGHT_TICKET)
- Unit testovi: keygen, crypto, model, middleware (24 testa)
- Dokumentacija: SPEC.md, ARCHITECTURE.md, SETUP.md, API.md, TESTING.md, README.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 07:42:25 +00:00

65 lines
1.3 KiB
Go

package middleware
import (
"net/http"
"sync"
"time"
)
type rateLimiter struct {
mu sync.Mutex
visitors map[string][]time.Time
limit int
window time.Duration
}
func newRateLimiter(limit int) *rateLimiter {
return &rateLimiter{
visitors: make(map[string][]time.Time),
limit: limit,
window: time.Minute,
}
}
func (rl *rateLimiter) allow(ip string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
now := time.Now()
cutoff := now.Add(-rl.window)
var valid []time.Time
for _, t := range rl.visitors[ip] {
if t.After(cutoff) {
valid = append(valid, t)
}
}
if len(valid) >= rl.limit {
rl.visitors[ip] = valid
return false
}
rl.visitors[ip] = append(valid, now)
return true
}
func RateLimit(limit int) func(http.Handler) http.Handler {
rl := newRateLimiter(limit)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
ip = xff
}
if !rl.allow(ip) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusTooManyRequests)
w.Write([]byte(`{"error":{"code":"RATE_LIMITED","message":"Previse zahteva, pokusajte ponovo za minut"}}`))
return
}
next.ServeHTTP(w, r)
})
}
}