54 lines
1.4 KiB
Go
54 lines
1.4 KiB
Go
package httpx
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// JSON پاسخ JSON با کد وضعیت مینویسد.
|
|
func JSON(w http.ResponseWriter, status int, body any) {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(body)
|
|
}
|
|
|
|
// Error پاسخ خطای استاندارد { "message": ... }.
|
|
func Error(w http.ResponseWriter, status int, msg string) {
|
|
JSON(w, status, map[string]string{"message": msg})
|
|
}
|
|
|
|
// Throttle محدودکننده ساده in-memory بر اساس کلید (مثل IP یا موبایل).
|
|
// معادل throttle:max,perMinutes در Laravel — بدون نیاز به Redis.
|
|
type Throttle struct {
|
|
mu sync.Mutex
|
|
hits map[string][]time.Time
|
|
max int
|
|
window time.Duration
|
|
}
|
|
|
|
func NewThrottle(max int, window time.Duration) *Throttle {
|
|
return &Throttle{hits: make(map[string][]time.Time), max: max, window: window}
|
|
}
|
|
|
|
// Allow بررسی میکند آیا کلید در پنجره زمانی مجاز است.
|
|
func (t *Throttle) Allow(key string) bool {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
now := time.Now()
|
|
cutoff := now.Add(-t.window)
|
|
kept := t.hits[key][:0]
|
|
for _, ts := range t.hits[key] {
|
|
if ts.After(cutoff) {
|
|
kept = append(kept, ts)
|
|
}
|
|
}
|
|
if len(kept) >= t.max {
|
|
t.hits[key] = kept
|
|
return false
|
|
}
|
|
t.hits[key] = append(kept, now)
|
|
return true
|
|
}
|