chore: initial commit of ai site platform
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
76
gateway/internal/ratelimit/limiter.go
Normal file
76
gateway/internal/ratelimit/limiter.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Limiter struct {
|
||||
mu sync.Mutex
|
||||
visitors map[string]*visitor
|
||||
rate int // 0 = disabled
|
||||
window time.Duration
|
||||
}
|
||||
|
||||
type visitor struct {
|
||||
count int
|
||||
reset time.Time
|
||||
}
|
||||
|
||||
// New creates a per-IP limiter. ratePerMinute <= 0 disables limiting.
|
||||
func New(ratePerMinute int) *Limiter {
|
||||
return &Limiter{visitors: map[string]*visitor{}, rate: ratePerMinute, window: time.Minute}
|
||||
}
|
||||
|
||||
func (l *Limiter) Middleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if l.rate <= 0 {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil || ip == "" {
|
||||
ip = r.RemoteAddr
|
||||
}
|
||||
// 本机开发:Vite 代理与所有本机请求共用 127.0.0.1,不做限流
|
||||
if isLoopback(ip) {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
if !l.allow(ip) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
_, _ = w.Write([]byte(`{"code":429,"message":"gateway rate limit exceeded"}`))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func isLoopback(ip string) bool {
|
||||
ip = strings.Trim(ip, "[]")
|
||||
if ip == "127.0.0.1" || ip == "::1" || ip == "localhost" {
|
||||
return true
|
||||
}
|
||||
parsed := net.ParseIP(ip)
|
||||
return parsed != nil && parsed.IsLoopback()
|
||||
}
|
||||
|
||||
func (l *Limiter) allow(key string) bool {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
now := time.Now()
|
||||
v, ok := l.visitors[key]
|
||||
if !ok || now.After(v.reset) {
|
||||
l.visitors[key] = &visitor{count: 1, reset: now.Add(l.window)}
|
||||
return true
|
||||
}
|
||||
if v.count >= l.rate {
|
||||
return false
|
||||
}
|
||||
v.count++
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user