package ws import ( "log" "sync" "tailly_back_v2/internal/domain" "time" ) type Client struct { UserID int Send chan *domain.Message LastSeen time.Time CloseChan chan bool } type Hub struct { clients map[int]*Client register chan *Client unregister chan *Client broadcast chan *domain.Message mu sync.RWMutex } func NewHub() *Hub { hub := &Hub{ clients: make(map[int]*Client), register: make(chan *Client), unregister: make(chan *Client), broadcast: make(chan *domain.Message, 100), } go hub.cleanupInactiveClients() return hub } // Добавляем периодическую очистку неактивных клиентов func (h *Hub) cleanupInactiveClients() { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for { select { case <-ticker.C: h.mu.Lock() for userID, client := range h.clients { if time.Since(client.LastSeen) > 10*time.Minute { log.Printf("Cleaning up inactive client: %d", userID) close(client.Send) delete(h.clients, userID) } } h.mu.Unlock() } } } func (h *Hub) RegisterClient(client *Client) { client.LastSeen = time.Now() h.register <- client } func (h *Hub) UnregisterClient(client *Client) { h.unregister <- client } func (h *Hub) Broadcast(message *domain.Message) { if message == nil || message.SenderID == 0 { log.Println("Attempt to broadcast invalid message") return } if message.ReceiverID == 0 { log.Printf("Message %d has no receiver", message.ID) return } h.broadcast <- message } func (h *Hub) Run() { for { select { case client := <-h.register: h.mu.Lock() // Обновляем LastSeen при регистрации client.LastSeen = time.Now() // Закрываем предыдущее соединение если есть if existing, ok := h.clients[client.UserID]; ok { select { case existing.CloseChan <- true: // Уведомляем о закрытии default: } close(existing.Send) } h.clients[client.UserID] = client h.mu.Unlock() case client := <-h.unregister: h.mu.Lock() if c, ok := h.clients[client.UserID]; ok && c == client { close(c.Send) delete(h.clients, client.UserID) } h.mu.Unlock() case message := <-h.broadcast: h.mu.RLock() // Отправляем всем клиентам, кто участвует в этом чате for _, client := range h.clients { if client.UserID == message.SenderID || client.UserID == message.ReceiverID { client.LastSeen = time.Now() // Обновляем время активности select { case client.Send <- message: // Сообщение успешно отправлено default: log.Printf("Client %d channel busy, skipping message", client.UserID) // Не закрываем соединение, просто пропускаем сообщение } } } h.mu.RUnlock() } } }