added discord api limiting

This commit is contained in:
2026-01-24 00:58:31 +11:00
parent af6bec983b
commit ff0f61f534
15 changed files with 1363 additions and 141 deletions

View File

@@ -0,0 +1,95 @@
package store
import (
"net"
"net/http"
"strings"
"time"
)
// getClientIP extracts the client IP address, checking X-Forwarded-For first
func getClientIP(r *http.Request) string {
// Check X-Forwarded-For header (comma-separated list, first is client)
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
// Take the first IP in the list
ips := strings.Split(xff, ",")
if len(ips) > 0 {
return strings.TrimSpace(ips[0])
}
}
// Fall back to RemoteAddr (format: "IP:port" or "[IPv6]:port")
// Use net.SplitHostPort to properly handle both IPv4 and IPv6
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
// If SplitHostPort fails, return as-is (shouldn't happen with valid RemoteAddr)
return r.RemoteAddr
}
return host
}
// TrackRedirect increments the redirect counter for this IP+UA+Path combination
// Returns the current attempt count, whether limit was exceeded, and the track details
func (s *Store) TrackRedirect(r *http.Request, path string, maxAttempts int) (attempts int, exceeded bool, track *RedirectTrack) {
if r == nil {
return 0, false, nil
}
ip := getClientIP(r)
userAgent := r.UserAgent()
key := redirectKey(ip, userAgent, path)
now := time.Now()
expiresAt := now.Add(5 * time.Minute)
// Try to load existing track
val, exists := s.redirectTracks.Load(key)
if exists {
track = val.(*RedirectTrack)
// Check if expired
if now.After(track.ExpiresAt) {
// Expired, start fresh
track = &RedirectTrack{
IP: ip,
UserAgent: userAgent,
Path: path,
Attempts: 1,
FirstSeen: now,
ExpiresAt: expiresAt,
}
s.redirectTracks.Store(key, track)
return 1, false, track
}
// Increment existing
track.Attempts++
track.ExpiresAt = expiresAt // Extend expiry
exceeded = track.Attempts >= maxAttempts
return track.Attempts, exceeded, track
}
// Create new track
track = &RedirectTrack{
IP: ip,
UserAgent: userAgent,
Path: path,
Attempts: 1,
FirstSeen: now,
ExpiresAt: expiresAt,
}
s.redirectTracks.Store(key, track)
return 1, false, track
}
// ClearRedirectTrack removes a redirect tracking entry (called after successful completion)
func (s *Store) ClearRedirectTrack(r *http.Request, path string) {
if r == nil {
return
}
ip := getClientIP(r)
userAgent := r.UserAgent()
key := redirectKey(ip, userAgent, path)
s.redirectTracks.Delete(key)
}