- Implement full MessageHandler dispatch with all 18 incoming message types - Add handler_messages.go: SendMessage, EditMessage, DeleteMessage with ownership checks - Add handler_rooms.go: JoinConversation, LeaveConversation - Add handler_history.go: FetchHistory (cursor-based), SearchMessages (ILIKE), SyncMessages - Add handler_realtime.go: Typing, MarkAsRead, Delivered, AddReaction, RemoveReaction - Add handler_calls.go: WebRTC signaling relay (CallOffer/Answer/ICE/Hangup/Reject) - Add PermissionService: CanRead/CanSend/CanJoin/CanModerate based on room_members - Add RateLimiter: per-user per-action sliding window (in-memory) - Wire all dependencies in router.go setupChatWebSocket
62 lines
1.1 KiB
Go
62 lines
1.1 KiB
Go
package chat
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type RateLimiter struct {
|
|
limits map[string]rateConfig
|
|
entries map[string]*rateBucket
|
|
mu sync.Mutex
|
|
}
|
|
|
|
type rateConfig struct {
|
|
maxRequests int
|
|
window time.Duration
|
|
}
|
|
|
|
type rateBucket struct {
|
|
count int
|
|
windowAt time.Time
|
|
}
|
|
|
|
func NewRateLimiter() *RateLimiter {
|
|
return &RateLimiter{
|
|
limits: map[string]rateConfig{
|
|
"send_message": {maxRequests: 10, window: time.Second},
|
|
"typing": {maxRequests: 5, window: time.Second},
|
|
"search": {maxRequests: 2, window: time.Second},
|
|
"fetch_history": {maxRequests: 5, window: time.Second},
|
|
},
|
|
entries: make(map[string]*rateBucket),
|
|
}
|
|
}
|
|
|
|
func (rl *RateLimiter) Allow(userID uuid.UUID, action string) bool {
|
|
cfg, ok := rl.limits[action]
|
|
if !ok {
|
|
return true
|
|
}
|
|
|
|
key := userID.String() + ":" + action
|
|
now := time.Now()
|
|
|
|
rl.mu.Lock()
|
|
defer rl.mu.Unlock()
|
|
|
|
bucket, exists := rl.entries[key]
|
|
if !exists || now.Sub(bucket.windowAt) > cfg.window {
|
|
rl.entries[key] = &rateBucket{count: 1, windowAt: now}
|
|
return true
|
|
}
|
|
|
|
if bucket.count >= cfg.maxRequests {
|
|
return false
|
|
}
|
|
|
|
bucket.count++
|
|
return true
|
|
}
|