v0.0.17.3 Переработан websocket, добавлена обработка ping/pong
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
madipo2611 2025-08-11 15:49:16 +03:00
parent 039c8447a7
commit db667f224b
4 changed files with 46 additions and 13 deletions

View File

@ -159,17 +159,22 @@ func (r *mutationResolver) SendMessage(ctx context.Context, receiverID int, cont
return nil, errors.New("не авторизован")
}
// Проверяем, что не отправляем сообщение себе
if senderID == receiverID {
return nil, errors.New("cannot send message to yourself")
}
chat, err := r.Services.Chat.GetOrCreateChat(ctx, senderID, receiverID)
if err != nil {
return nil, fmt.Errorf("ошибка создания чата: %v", err)
}
return r.Services.Chat.SendMessage(ctx, senderID, chat.ID, content)
message, err := r.Services.Chat.SendMessage(ctx, senderID, chat.ID, content)
if err != nil {
return nil, err
}
// Рассылаем сообщение через WebSocket
if r.Services.ChatHub != nil {
r.Services.ChatHub.Broadcast(message)
}
return message, nil
}
// MarkAsRead - помечает сообщение как прочитанное

View File

@ -9,6 +9,7 @@ import (
"tailly_back_v2/internal/service"
"tailly_back_v2/internal/ws"
"tailly_back_v2/pkg/auth"
"time"
"github.com/gorilla/websocket"
)
@ -105,13 +106,17 @@ func (h *ChatHandler) HandleWebSocket(w http.ResponseWriter, r *http.Request) {
}
func (h *ChatHandler) readPump(ctx context.Context, conn *websocket.Conn, client *ws.Client, userID int) {
defer func() {
h.hub.UnregisterClient(client)
conn.Close()
}()
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// Отправляем ping
if err := conn.WriteJSON(map[string]string{"type": "ping"}); err != nil {
log.Printf("Ping error: %v", err)
return
}
case <-ctx.Done():
return
default:
@ -125,11 +130,15 @@ func (h *ChatHandler) readPump(ctx context.Context, conn *websocket.Conn, client
if err := conn.ReadJSON(&msg); err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
log.Printf("WebSocket read error: %v", err)
log.Printf("WebSocket error: %v", err)
}
return
}
if msg.Type == "pong" {
continue
}
// Обработка ping/pong
if msg.Type == "ping" {
conn.WriteJSON(map[string]string{"type": "pong"})

View File

@ -39,30 +39,38 @@ func (s *chatService) SendMessage(ctx context.Context, senderID, chatID int, con
return nil, err
}
// Проверяем, что отправитель является участником чата
if senderID != chat.User1ID && senderID != chat.User2ID {
return nil, errors.New("user is not a participant of this chat")
}
// Определяем получателя
receiverID := chat.User1ID
if senderID == chat.User1ID {
receiverID = chat.User2ID
}
// Создаем сообщение
message := &domain.Message{
ChatID: chatID,
SenderID: senderID,
ReceiverID: receiverID,
ReceiverID: receiverID, // Гарантируем что receiverID всегда установлен
Content: content,
Status: "sent",
CreatedAt: time.Now(),
}
// Сохраняем в БД
if err := s.chatRepo.SaveMessage(ctx, message); err != nil {
return nil, err
}
// Отправляем сообщение через WebSocket
// Отправляем через WebSocket только один раз
if s.hub != nil {
// Добавляем проверку перед рассылкой
if message.ReceiverID == 0 {
return nil, errors.New("receiver ID is required")
}
s.hub.Broadcast(message)
}

View File

@ -1,6 +1,7 @@
package ws
import (
"log"
"sync"
"tailly_back_v2/internal/domain"
)
@ -36,6 +37,16 @@ func (h *Hub) UnregisterClient(client *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
}