v0.0.18 Удален текущий сервис messages и реализован новый на базе микросервиса gRPC
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
aa623ff1c4
commit
dcb1cccb52
1
.env
1
.env
@ -11,3 +11,4 @@ SMTP_USERNAME=info@tailly.ru
|
||||
SMTP_PASSWORD="U8c64CyoD928cqij"
|
||||
SMTP_FROM=info@tailly.ru
|
||||
AppURL="https://tailly.ru"
|
||||
MESSAGE_SERVICE_ADDRESS="localhost:50051"
|
||||
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"google.golang.org/grpc"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
@ -10,9 +11,9 @@ import (
|
||||
"tailly_back_v2/internal/http"
|
||||
"tailly_back_v2/internal/repository"
|
||||
"tailly_back_v2/internal/service"
|
||||
"tailly_back_v2/internal/ws"
|
||||
"tailly_back_v2/pkg/auth"
|
||||
"tailly_back_v2/pkg/database"
|
||||
"tailly_back_v2/proto"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -30,6 +31,14 @@ func main() {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Подключение к gRPC серверу сообщений
|
||||
grpcConn, err := grpc.Dial(cfg.GRPC.MessageServiceAddress, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to connect to messages gRPC service: %v", err)
|
||||
}
|
||||
defer grpcConn.Close()
|
||||
messageClient := proto.NewMessageServiceClient(grpcConn)
|
||||
|
||||
// Инициализация зависимостей
|
||||
tokenAuth := auth.NewTokenAuth(
|
||||
cfg.Auth.AccessTokenSecret,
|
||||
@ -38,16 +47,11 @@ func main() {
|
||||
cfg.Auth.RefreshTokenExpiry,
|
||||
)
|
||||
|
||||
// Инициализация чата
|
||||
chatHub := ws.NewHub()
|
||||
go chatHub.Run()
|
||||
|
||||
// Репозитории
|
||||
userRepo := repository.NewUserRepository(db)
|
||||
postRepo := repository.NewPostRepository(db)
|
||||
commentRepo := repository.NewCommentRepository(db)
|
||||
likeRepo := repository.NewLikeRepository(db)
|
||||
chatRepo := repository.NewChatRepository(db)
|
||||
auditRepo := repository.NewAuditRepository(db)
|
||||
recoveryRepo := repository.NewRecoveryRepository(db)
|
||||
deviceRepo := repository.NewDeviceRepository(db)
|
||||
@ -72,7 +76,6 @@ func main() {
|
||||
postService := service.NewPostService(postRepo)
|
||||
commentService := service.NewCommentService(commentRepo, postRepo)
|
||||
likeService := service.NewLikeService(likeRepo, postRepo)
|
||||
chatService := service.NewChatService(chatRepo, userRepo, chatHub)
|
||||
auditService := service.NewAuditService(auditRepo)
|
||||
recoveryService := service.NewRecoveryService(recoveryRepo, userRepo, sessionRepo, deviceRepo, mailService)
|
||||
sessionService := service.NewSessionService(sessionRepo, deviceRepo, userRepo, mailService)
|
||||
@ -84,12 +87,11 @@ func main() {
|
||||
Post: postService,
|
||||
Comment: commentService,
|
||||
Like: likeService,
|
||||
Chat: chatService,
|
||||
Audit: auditService,
|
||||
Recovery: recoveryService,
|
||||
Session: sessionService,
|
||||
Mail: mailService,
|
||||
ChatHub: chatHub, // Добавляем хаб в Services
|
||||
Messages: messageClient, // Добавляем gRPC клиент
|
||||
}
|
||||
|
||||
// HTTP сервер - передаем db как дополнительный параметр
|
||||
@ -101,7 +103,6 @@ func main() {
|
||||
if err := server.Run(); err != nil {
|
||||
log.Printf("server error: %v", err)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
// Ожидание сигнала завершения
|
||||
|
||||
@ -28,6 +28,9 @@ type Config struct {
|
||||
From string `env:"SMTP_FROM,required"`
|
||||
URL string `env:"AppURL,required"`
|
||||
}
|
||||
GRPC struct {
|
||||
MessageServiceAddress string `env:"MESSAGE_SERVICE_ADDRESS"`
|
||||
}
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
|
||||
@ -3,21 +3,23 @@ package domain
|
||||
import "time"
|
||||
|
||||
type Chat struct {
|
||||
ID int `json:"id"`
|
||||
User1ID int `json:"user1Id"`
|
||||
User2ID int `json:"user2Id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ID int `json:"id"`
|
||||
User1ID int `json:"user1Id"`
|
||||
User2ID int `json:"user2Id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
LastMessage *Message `json:"lastMessage"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
ID int `json:"id"`
|
||||
ChatID int `json:"chatId"`
|
||||
SenderID int `json:"senderId"`
|
||||
ReceiverID int `json:"receiverId"`
|
||||
Content string `json:"content"`
|
||||
Status string `json:"status"` // "sent", "delivered", "read"
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ID int `json:"id"`
|
||||
ChatID int `json:"chatId"`
|
||||
SenderID int `json:"senderId"`
|
||||
Content string `json:"content"`
|
||||
Status string `json:"status"` // "sent", "delivered", "read"
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
type ChatSession struct {
|
||||
User *User `json:"user"`
|
||||
LastMessage *Message `json:"lastMessage"`
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,247 +0,0 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"tailly_back_v2/internal/domain"
|
||||
"tailly_back_v2/internal/ws"
|
||||
"time"
|
||||
)
|
||||
|
||||
type messageResolver struct{ *Resolver }
|
||||
|
||||
// GetChatHistory - возвращает историю сообщений
|
||||
func (r *queryResolver) GetChatHistory(ctx context.Context, userID int) ([]*domain.Message, error) {
|
||||
currentUserID, err := getUserIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.New("не авторизован")
|
||||
}
|
||||
|
||||
chat, err := r.Services.Chat.GetOrCreateChat(ctx, currentUserID, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения чата: %v", err)
|
||||
}
|
||||
|
||||
messages, err := r.Services.Chat.GetChatMessages(ctx, chat.ID, currentUserID, 50, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения сообщений: %v", err)
|
||||
}
|
||||
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
// GetUserChats - возвращает чаты пользователя
|
||||
func (r *queryResolver) GetUserChats(ctx context.Context) ([]*ChatSession, error) {
|
||||
userID, err := getUserIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.New("не авторизован")
|
||||
}
|
||||
|
||||
// Проверяем инициализацию сервиса
|
||||
if r.Services == nil || r.Services.Chat == nil {
|
||||
return nil, errors.New("chat service not initialized")
|
||||
}
|
||||
|
||||
// Получаем чаты пользователя
|
||||
chats, err := r.Services.Chat.GetUserChats(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения чатов: %v", err)
|
||||
}
|
||||
|
||||
var sessions []*ChatSession
|
||||
for _, chat := range chats {
|
||||
// Определяем другого участника чата
|
||||
otherUserID := chat.User1ID
|
||||
if userID == chat.User1ID {
|
||||
otherUserID = chat.User2ID
|
||||
}
|
||||
|
||||
// Получаем данные другого пользователя
|
||||
otherUser, err := r.Services.User.GetByID(ctx, otherUserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения пользователя %d: %v", otherUserID, err)
|
||||
}
|
||||
|
||||
// Получаем последнее сообщение
|
||||
messages, err := r.Services.Chat.GetChatMessages(ctx, chat.ID, userID, 1, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения сообщений: %v", err)
|
||||
}
|
||||
|
||||
var lastMessage *domain.Message
|
||||
if len(messages) > 0 {
|
||||
lastMessage = messages[0]
|
||||
} else {
|
||||
// Создаем пустое сообщение, если чат новый
|
||||
lastMessage = &domain.Message{
|
||||
ChatID: chat.ID,
|
||||
Content: "Чат создан",
|
||||
Status: "system",
|
||||
CreatedAt: chat.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Получаем количество непрочитанных сообщений
|
||||
unreadCount, err := r.Services.Chat.GetUnreadCount(ctx, chat.ID, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения количества непрочитанных: %v", err)
|
||||
}
|
||||
|
||||
sessions = append(sessions, &ChatSession{
|
||||
User: otherUser,
|
||||
LastMessage: lastMessage,
|
||||
UnreadCount: unreadCount,
|
||||
})
|
||||
}
|
||||
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// Sender - возвращает отправителя сообщения
|
||||
func (r *messageResolver) Sender(ctx context.Context, obj *domain.Message) (*domain.User, error) {
|
||||
user, err := r.Services.User.GetByID(ctx, obj.SenderID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения отправителя: %v", err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *messageResolver) Receiver(ctx context.Context, obj *domain.Message) (*domain.User, error) {
|
||||
// 1. Если receiver явно указан в сообщении
|
||||
if obj.ReceiverID != 0 {
|
||||
return r.Services.User.GetByID(ctx, obj.ReceiverID)
|
||||
}
|
||||
|
||||
// 2. Если есть chat, определяем получателя через чат
|
||||
if obj.ChatID != 0 {
|
||||
chat, err := r.chatRepo.GetChatByID(ctx, obj.ChatID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get chat: %v", err)
|
||||
}
|
||||
|
||||
// Определяем получателя
|
||||
receiverID := chat.User1ID
|
||||
if obj.SenderID == chat.User1ID {
|
||||
receiverID = chat.User2ID
|
||||
}
|
||||
return r.Services.User.GetByID(ctx, receiverID)
|
||||
}
|
||||
|
||||
// 3. Если ничего не найдено
|
||||
return nil, fmt.Errorf("cannot determine receiver - both receiver_id and chat_id are not set")
|
||||
}
|
||||
|
||||
// CreatedAt - форматирует время сообщения
|
||||
func (r *messageResolver) CreatedAt(ctx context.Context, obj *domain.Message) (string, error) {
|
||||
return obj.CreatedAt.Format(time.RFC3339), nil
|
||||
}
|
||||
|
||||
// SendMessage - отправляет сообщение
|
||||
func (r *mutationResolver) SendMessage(ctx context.Context, receiverID int, content string) (*domain.Message, error) {
|
||||
senderID, err := getUserIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.New("не авторизован")
|
||||
}
|
||||
|
||||
chat, err := r.Services.Chat.GetOrCreateChat(ctx, senderID, receiverID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка создания чата: %v", err)
|
||||
}
|
||||
|
||||
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 - помечает сообщение как прочитанное
|
||||
func (r *mutationResolver) MarkAsRead(ctx context.Context, messageID int) (bool, error) {
|
||||
if r.Services == nil || r.Services.Chat == nil {
|
||||
return false, errors.New("сервис чатов не инициализирован")
|
||||
}
|
||||
|
||||
// Все операции через сервис
|
||||
err := r.Services.Chat.MarkAsRead(ctx, messageID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("ошибка отметки как прочитанного: %v", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type subscriptionResolver struct{ *Resolver }
|
||||
|
||||
// Subscription returns SubscriptionResolver implementation.
|
||||
func (r *Resolver) Subscription() SubscriptionResolver { return &subscriptionResolver{r} }
|
||||
|
||||
// MessageReceived - подписка на новые сообщения
|
||||
func (r *subscriptionResolver) MessageReceived(ctx context.Context) (<-chan *domain.Message, error) {
|
||||
userID, err := getUserIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.New("не авторизован")
|
||||
}
|
||||
|
||||
messageChan := make(chan *domain.Message, 1)
|
||||
|
||||
// Создаем клиента для хаба
|
||||
client := &ws.Client{
|
||||
UserID: userID,
|
||||
Send: messageChan,
|
||||
}
|
||||
|
||||
// Регистрируем клиента в хабе
|
||||
r.Services.ChatHub.RegisterClient(client)
|
||||
|
||||
// Горутина для обработки отключения
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
r.Services.ChatHub.UnregisterClient(client)
|
||||
close(messageChan)
|
||||
}()
|
||||
|
||||
return messageChan, nil
|
||||
}
|
||||
|
||||
// CreateChat is the resolver for the createChat field.
|
||||
func (r *mutationResolver) CreateChat(ctx context.Context, userID int) (*ChatSession, error) {
|
||||
currentUserID, err := getUserIDFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.New("не авторизован")
|
||||
}
|
||||
|
||||
// Создаем или получаем существующий чат
|
||||
chat, err := r.Services.Chat.GetOrCreateChat(ctx, currentUserID, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка создания чата: %v", err)
|
||||
}
|
||||
|
||||
// Получаем данные другого пользователя
|
||||
otherUser, err := r.Services.User.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения пользователя: %v", err)
|
||||
}
|
||||
|
||||
// Создаем пустое последнее сообщение (или можно вернуть nil, если схема позволяет)
|
||||
emptyMessage := &domain.Message{
|
||||
ChatID: chat.ID,
|
||||
SenderID: currentUserID,
|
||||
ReceiverID: otherUser.ID,
|
||||
Content: "Чат создан",
|
||||
Status: "system",
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
return &ChatSession{
|
||||
User: otherUser,
|
||||
LastMessage: emptyMessage,
|
||||
UnreadCount: 0,
|
||||
}, nil
|
||||
}
|
||||
201
internal/http/graph/messages_resolvers.go
Normal file
201
internal/http/graph/messages_resolvers.go
Normal file
@ -0,0 +1,201 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tailly_back_v2/internal/domain"
|
||||
"tailly_back_v2/proto"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Subscription возвращает реализацию SubscriptionResolver
|
||||
func (r *Resolver) Subscription() SubscriptionResolver {
|
||||
return &subscriptionResolver{r}
|
||||
}
|
||||
|
||||
type subscriptionResolver struct{ *Resolver }
|
||||
|
||||
// CreateChat is the resolver for the createChat field.
|
||||
func (r *mutationResolver) CreateChat(ctx context.Context, user1Id int, user2Id int) (*domain.Chat, error) {
|
||||
// Просто вызываем gRPC метод, вся логика уже там
|
||||
res, err := r.MessageClient.CreateChat(ctx, &proto.CreateChatRequest{
|
||||
User1Id: int32(user1Id),
|
||||
User2Id: int32(user2Id),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create chat: %w", err)
|
||||
}
|
||||
|
||||
// Преобразуем proto-чат в domain-модель
|
||||
return protoChatToDomain(res.Chat), nil
|
||||
}
|
||||
|
||||
// SendMessage реализация мутации для отправки сообщения
|
||||
func (r *mutationResolver) SendMessage(ctx context.Context, chatID int, senderID int, content string) (*domain.Message, error) {
|
||||
res, err := r.MessageClient.SendMessage(ctx, &proto.SendMessageRequest{
|
||||
ChatId: int32(chatID),
|
||||
SenderId: int32(senderID),
|
||||
Content: content,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send message: %w", err)
|
||||
}
|
||||
return protoMessageToDomain(res.Message), nil
|
||||
}
|
||||
|
||||
// UpdateMessageStatus реализация мутации для обновления статуса сообщения
|
||||
func (r *mutationResolver) UpdateMessageStatus(ctx context.Context, messageID int, status MessageStatus) (*domain.Message, error) {
|
||||
var statusStr string
|
||||
switch status {
|
||||
case MessageStatusSent:
|
||||
statusStr = "SENT"
|
||||
case MessageStatusDelivered:
|
||||
statusStr = "DELIVERED"
|
||||
case MessageStatusRead:
|
||||
statusStr = "READ"
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown message status: %v", status)
|
||||
}
|
||||
|
||||
res, err := r.MessageClient.UpdateMessageStatus(ctx, &proto.UpdateMessageStatusRequest{
|
||||
MessageId: int32(messageID),
|
||||
Status: statusStr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update message status: %w", err)
|
||||
}
|
||||
|
||||
return protoMessageToDomain(res.Message), nil
|
||||
}
|
||||
|
||||
// GetChat реализация запроса для получения чата
|
||||
func (r *queryResolver) GetChat(ctx context.Context, user1Id int, user2Id int) (*domain.Chat, error) {
|
||||
res, err := r.MessageClient.GetChat(ctx, &proto.GetChatRequest{
|
||||
User1Id: int32(user1Id),
|
||||
User2Id: int32(user2Id),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get chat: %w", err)
|
||||
}
|
||||
return protoChatToDomain(res.Chat), nil
|
||||
}
|
||||
|
||||
// GetChatMessages реализация запроса для получения сообщений чата
|
||||
func (r *queryResolver) GetChatMessages(ctx context.Context, chatID int, limit int, offset int) ([]*domain.Message, error) {
|
||||
res, err := r.MessageClient.GetChatMessages(ctx, &proto.GetChatMessagesRequest{
|
||||
ChatId: int32(chatID),
|
||||
Limit: int32(limit),
|
||||
Offset: int32(offset),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get chat messages: %w", err)
|
||||
}
|
||||
var messages []*domain.Message
|
||||
for _, msg := range res.Messages {
|
||||
messages = append(messages, protoMessageToDomain(msg))
|
||||
}
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
// GetUserChats реализация запроса для получения чатов пользователя
|
||||
func (r *queryResolver) GetUserChats(ctx context.Context, userID int) ([]*domain.Chat, error) {
|
||||
res, err := r.MessageClient.GetUserChats(ctx, &proto.GetUserChatsRequest{
|
||||
UserId: int32(userID),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get user chats: %w", err)
|
||||
}
|
||||
var chats []*domain.Chat
|
||||
for _, chat := range res.Chats {
|
||||
chats = append(chats, protoChatToDomain(chat))
|
||||
}
|
||||
return chats, nil
|
||||
}
|
||||
|
||||
// MessageStream реализация подписки на новые сообщения
|
||||
func (r *subscriptionResolver) MessageStream(ctx context.Context, userID int) (<-chan *domain.Message, error) {
|
||||
stream, err := r.MessageClient.StreamMessages(ctx, &proto.StreamMessagesRequest{
|
||||
UserId: int32(userID),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to stream messages: %w", err)
|
||||
}
|
||||
|
||||
messageChan := make(chan *domain.Message)
|
||||
go func() {
|
||||
defer close(messageChan)
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case messageChan <- protoMessageToDomain(msg.Message):
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return messageChan, nil
|
||||
}
|
||||
|
||||
// Преобразование proto-сообщения в domain-модель
|
||||
func protoMessageToDomain(msg *proto.Message) *domain.Message {
|
||||
return &domain.Message{
|
||||
ID: int(msg.Id),
|
||||
ChatID: int(msg.ChatId),
|
||||
SenderID: int(msg.SenderId),
|
||||
Content: msg.Content,
|
||||
Status: msg.Status,
|
||||
CreatedAt: msg.CreatedAt.AsTime(),
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразование proto-чата в domain-модель
|
||||
func protoChatToDomain(chat *proto.Chat) *domain.Chat {
|
||||
gqlChat := &domain.Chat{
|
||||
ID: int(chat.Id),
|
||||
User1ID: int(chat.User1Id),
|
||||
User2ID: int(chat.User2Id),
|
||||
CreatedAt: chat.CreatedAt.AsTime(),
|
||||
UpdatedAt: chat.UpdatedAt.AsTime(),
|
||||
}
|
||||
|
||||
if chat.LastMessage != nil {
|
||||
gqlChat.LastMessage = protoMessageToDomain(chat.LastMessage)
|
||||
}
|
||||
|
||||
return gqlChat
|
||||
}
|
||||
|
||||
// Реализации резолверов для отдельных полей
|
||||
|
||||
type chatResolver struct{ *Resolver }
|
||||
type messageResolver struct{ *Resolver }
|
||||
|
||||
// Chat возвращает ChatResolver
|
||||
func (r *Resolver) Chat() ChatResolver { return &chatResolver{r} }
|
||||
|
||||
// Message возвращает MessageResolver
|
||||
func (r *Resolver) Message() MessageResolver { return &messageResolver{r} }
|
||||
|
||||
// CreatedAt для Chat
|
||||
func (r *chatResolver) CreatedAt(ctx context.Context, obj *domain.Chat) (string, error) {
|
||||
return obj.CreatedAt.Format(time.RFC3339), nil
|
||||
}
|
||||
|
||||
// UpdatedAt для Chat
|
||||
func (r *chatResolver) UpdatedAt(ctx context.Context, obj *domain.Chat) (string, error) {
|
||||
return obj.UpdatedAt.Format(time.RFC3339), nil
|
||||
}
|
||||
|
||||
// Status для Message
|
||||
func (r *messageResolver) Status(ctx context.Context, obj *domain.Message) (MessageStatus, error) {
|
||||
return MessageStatus(obj.Status), nil
|
||||
}
|
||||
|
||||
// CreatedAt для Message
|
||||
func (r *messageResolver) CreatedAt(ctx context.Context, obj *domain.Message) (string, error) {
|
||||
return obj.CreatedAt.Format(time.RFC3339), nil
|
||||
}
|
||||
@ -3,15 +3,12 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"tailly_back_v2/internal/domain"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ChatSession struct {
|
||||
User *domain.User `json:"user"`
|
||||
LastMessage *domain.Message `json:"lastMessage"`
|
||||
UnreadCount int `json:"unreadCount"`
|
||||
}
|
||||
|
||||
type Mutation struct {
|
||||
}
|
||||
|
||||
@ -20,3 +17,60 @@ type Query struct {
|
||||
|
||||
type Subscription struct {
|
||||
}
|
||||
|
||||
type MessageStatus string
|
||||
|
||||
const (
|
||||
MessageStatusSent MessageStatus = "SENT"
|
||||
MessageStatusDelivered MessageStatus = "DELIVERED"
|
||||
MessageStatusRead MessageStatus = "READ"
|
||||
)
|
||||
|
||||
var AllMessageStatus = []MessageStatus{
|
||||
MessageStatusSent,
|
||||
MessageStatusDelivered,
|
||||
MessageStatusRead,
|
||||
}
|
||||
|
||||
func (e MessageStatus) IsValid() bool {
|
||||
switch e {
|
||||
case MessageStatusSent, MessageStatusDelivered, MessageStatusRead:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e MessageStatus) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *MessageStatus) UnmarshalGQL(v any) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = MessageStatus(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid MessageStatus", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e MessageStatus) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
func (e *MessageStatus) UnmarshalJSON(b []byte) error {
|
||||
s, err := strconv.Unquote(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.UnmarshalGQL(s)
|
||||
}
|
||||
|
||||
func (e MessageStatus) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
e.MarshalGQL(&buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"tailly_back_v2/internal/repository"
|
||||
"tailly_back_v2/internal/service"
|
||||
"tailly_back_v2/proto"
|
||||
)
|
||||
|
||||
// This file will not be regenerated automatically.
|
||||
@ -14,18 +15,20 @@ import (
|
||||
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||
|
||||
type Resolver struct {
|
||||
Services *service.Services
|
||||
DeviceRepo repository.DeviceRepository // Добавляем репозиторий устройств напрямую
|
||||
chatRepo repository.ChatRepository
|
||||
Services *service.Services
|
||||
DeviceRepo repository.DeviceRepository // Добавляем репозиторий устройств напрямую
|
||||
MessageClient proto.MessageServiceClient
|
||||
}
|
||||
|
||||
func NewResolver(
|
||||
services *service.Services,
|
||||
db *sql.DB, // Принимаем подключение к БД
|
||||
messageClient proto.MessageServiceClient,
|
||||
) *Resolver {
|
||||
return &Resolver{
|
||||
Services: services,
|
||||
DeviceRepo: repository.NewDeviceRepository(db), // Инициализируем репозиторий
|
||||
Services: services,
|
||||
DeviceRepo: repository.NewDeviceRepository(db),
|
||||
MessageClient: messageClient,
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,8 +36,7 @@ func NewResolver(
|
||||
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
||||
|
||||
// Query returns QueryResolver implementation.
|
||||
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
||||
func (r *Resolver) Message() MessageResolver { return &messageResolver{r} }
|
||||
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
|
||||
|
||||
@ -64,19 +64,29 @@ input LoginInput {
|
||||
|
||||
type Message {
|
||||
id: Int!
|
||||
sender: User!
|
||||
receiver: User!
|
||||
chatId: Int!
|
||||
senderId: Int!
|
||||
content: String!
|
||||
status: MessageStatus!
|
||||
createdAt: String!
|
||||
status: String!
|
||||
}
|
||||
|
||||
type ChatSession {
|
||||
user: User!
|
||||
lastMessage: Message!
|
||||
unreadCount: Int!
|
||||
enum MessageStatus {
|
||||
SENT
|
||||
DELIVERED
|
||||
READ
|
||||
}
|
||||
|
||||
type Chat {
|
||||
id: Int!
|
||||
user1Id: Int!
|
||||
user2Id: Int!
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
lastMessage: Message
|
||||
}
|
||||
|
||||
|
||||
type Session {
|
||||
id: Int!
|
||||
device: Device!
|
||||
@ -101,8 +111,9 @@ type Query {
|
||||
getUserPosts(userId: Int!): [Post!]!
|
||||
user(id: Int!): User! # Получить пользователя по ID
|
||||
users: [User!]!
|
||||
getChatHistory(userId: Int!): [Message!]!
|
||||
getUserChats: [ChatSession!]!
|
||||
getChat(user1Id: Int!, user2Id: Int!): Chat!
|
||||
getChatMessages(chatId: Int!, limit: Int!, offset: Int!): [Message!]!
|
||||
getUserChats(userId: Int!): [Chat!]!
|
||||
mySessions: [Session!]!
|
||||
comments(postID: Int!): [Comment!]!
|
||||
}
|
||||
@ -131,21 +142,20 @@ type Mutation {
|
||||
unlikePost(postId: Int!): Boolean!
|
||||
updateProfile(username: String!, email: String!, avatar: String!): User!
|
||||
changePassword(oldPassword: String!, newPassword: String!): Boolean!
|
||||
sendMessage(receiverId: Int!, content: String!): Message!
|
||||
markAsRead(messageId: Int!): Boolean!
|
||||
terminateSession(sessionId: Int!): Boolean!
|
||||
renameDevice(deviceId: Int!, name: String!): Device!
|
||||
# Запрос на подтверждение email
|
||||
requestEmailConfirmation: Boolean!
|
||||
|
||||
createChat(user1Id: Int!, user2Id: Int!): Chat!
|
||||
sendMessage(chatId: Int!, senderId: Int!, content: String!): Message!
|
||||
updateMessageStatus(messageId: Int!, status: MessageStatus!): Message!
|
||||
# Подтверждение email по токену
|
||||
confirmEmail(token: String!): Boolean!
|
||||
createChat(userId: Int!): ChatSession!
|
||||
# Повторная отправка подтверждения email
|
||||
resendEmailConfirmation: Boolean!
|
||||
deletePost(id: Int!): Boolean!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
messageReceived: Message!
|
||||
messageStream(userId: Int!): Message!
|
||||
}
|
||||
@ -12,11 +12,8 @@ import (
|
||||
"os"
|
||||
"tailly_back_v2/internal/config"
|
||||
"tailly_back_v2/internal/http/graph"
|
||||
"tailly_back_v2/internal/http/handlers"
|
||||
"tailly_back_v2/internal/http/middleware"
|
||||
"tailly_back_v2/internal/repository"
|
||||
"tailly_back_v2/internal/service"
|
||||
"tailly_back_v2/internal/ws"
|
||||
"tailly_back_v2/pkg/auth"
|
||||
)
|
||||
|
||||
@ -53,18 +50,6 @@ func (s *Server) configureRouter() {
|
||||
"http://localhost:3000",
|
||||
"https://tailly.ru",
|
||||
}
|
||||
// Инициализация WebSocket хаба
|
||||
hub := ws.NewHub()
|
||||
go hub.Run()
|
||||
|
||||
// Инициализация сервиса чата
|
||||
chatService := service.NewChatService(
|
||||
repository.NewChatRepository(s.db),
|
||||
repository.NewUserRepository(s.db),
|
||||
hub,
|
||||
)
|
||||
|
||||
s.services.Chat = chatService
|
||||
|
||||
logger := log.New(os.Stdout, "HTTP: ", log.LstdFlags)
|
||||
s.router.Use(middleware.WebSocketMiddleware)
|
||||
@ -73,7 +58,7 @@ func (s *Server) configureRouter() {
|
||||
s.router.Use(middleware.CORS(allowedOrigins))
|
||||
s.router.Use(middleware.AuthMiddleware(s.tokenAuth))
|
||||
|
||||
resolver := graph.NewResolver(s.services, s.db)
|
||||
resolver := graph.NewResolver(s.services, s.db, s.services.Messages)
|
||||
srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{
|
||||
Resolvers: resolver,
|
||||
}))
|
||||
@ -81,8 +66,7 @@ func (s *Server) configureRouter() {
|
||||
s.router.Handle("/", playground.Handler("GraphQL playground", "/query"))
|
||||
s.router.Handle("/query", srv)
|
||||
s.router.Handle("/uploads/*", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads"))))
|
||||
chatHandler := handlers.NewChatHandler(chatService, hub, s.tokenAuth)
|
||||
s.router.HandleFunc("/ws", chatHandler.HandleWebSocket)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) configureMetrics() {
|
||||
|
||||
@ -1,261 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"tailly_back_v2/internal/domain"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMessageNotFound = errors.New("message not found")
|
||||
ErrChatNotFound = errors.New("chat not found")
|
||||
)
|
||||
|
||||
type ChatRepository interface {
|
||||
// Основные методы сообщений
|
||||
SaveMessage(ctx context.Context, message *domain.Message) error
|
||||
GetMessageByID(ctx context.Context, id int) (*domain.Message, error)
|
||||
GetMessagesByChat(ctx context.Context, chatID int, limit, offset int) ([]*domain.Message, error)
|
||||
UpdateMessageStatus(ctx context.Context, id int, status string) error
|
||||
DeleteMessage(ctx context.Context, id int) error
|
||||
|
||||
// Методы чатов
|
||||
CreateChat(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error)
|
||||
GetChatByID(ctx context.Context, id int) (*domain.Chat, error)
|
||||
GetUserChats(ctx context.Context, userID int) ([]*domain.Chat, error)
|
||||
GetChatByParticipants(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error)
|
||||
GetUnreadCount(ctx context.Context, chatID, userID int) (int, error)
|
||||
}
|
||||
|
||||
type chatRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewChatRepository(db *sql.DB) ChatRepository {
|
||||
return &chatRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *chatRepository) SaveMessage(ctx context.Context, message *domain.Message) error {
|
||||
if message.ReceiverID == 0 {
|
||||
return errors.New("receiver_id is required")
|
||||
}
|
||||
|
||||
query := `
|
||||
INSERT INTO messages (chat_id, sender_id, receiver_id, content, status, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id
|
||||
`
|
||||
err := r.db.QueryRowContext(ctx, query,
|
||||
message.ChatID,
|
||||
message.SenderID,
|
||||
message.ReceiverID,
|
||||
message.Content,
|
||||
message.Status,
|
||||
message.CreatedAt,
|
||||
).Scan(&message.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *chatRepository) GetMessageByID(ctx context.Context, id int) (*domain.Message, error) {
|
||||
query := `
|
||||
SELECT id, chat_id, sender_id, content, status, created_at
|
||||
FROM messages
|
||||
WHERE id = $1
|
||||
`
|
||||
message := &domain.Message{}
|
||||
err := r.db.QueryRowContext(ctx, query, id).Scan(
|
||||
&message.ID,
|
||||
&message.ChatID,
|
||||
&message.SenderID,
|
||||
&message.Content,
|
||||
&message.Status,
|
||||
&message.CreatedAt,
|
||||
)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrMessageNotFound
|
||||
}
|
||||
return message, err
|
||||
}
|
||||
|
||||
func (r *chatRepository) GetMessagesByChat(ctx context.Context, chatID int, limit, offset int) ([]*domain.Message, error) {
|
||||
query := `
|
||||
SELECT id, chat_id, sender_id, content, status, created_at
|
||||
FROM messages
|
||||
WHERE chat_id = $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $2 OFFSET $3
|
||||
`
|
||||
rows, err := r.db.QueryContext(ctx, query, chatID, limit, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var messages []*domain.Message
|
||||
for rows.Next() {
|
||||
var message domain.Message
|
||||
if err := rows.Scan(
|
||||
&message.ID,
|
||||
&message.ChatID,
|
||||
&message.SenderID,
|
||||
&message.Content,
|
||||
&message.Status,
|
||||
&message.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
messages = append(messages, &message)
|
||||
}
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func (r *chatRepository) UpdateMessageStatus(ctx context.Context, id int, status string) error {
|
||||
query := `
|
||||
UPDATE messages
|
||||
SET status = $1
|
||||
WHERE id = $2
|
||||
`
|
||||
_, err := r.db.ExecContext(ctx, query, status, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *chatRepository) DeleteMessage(ctx context.Context, id int) error {
|
||||
query := `DELETE FROM messages WHERE id = $1`
|
||||
_, err := r.db.ExecContext(ctx, query, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *chatRepository) CreateChat(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error) {
|
||||
// Проверяем, что пользователи разные
|
||||
if user1ID == user2ID {
|
||||
return nil, errors.New("cannot create chat with yourself")
|
||||
}
|
||||
|
||||
// Проверяем существование пользователей
|
||||
if err := r.checkUserExists(ctx, user1ID); err != nil {
|
||||
return nil, fmt.Errorf("user1 does not exist: %v", err)
|
||||
}
|
||||
if err := r.checkUserExists(ctx, user2ID); err != nil {
|
||||
return nil, fmt.Errorf("user2 does not exist: %v", err)
|
||||
}
|
||||
|
||||
// Упорядочиваем ID пользователей согласно CHECK constraint
|
||||
if user1ID > user2ID {
|
||||
user1ID, user2ID = user2ID, user1ID
|
||||
}
|
||||
|
||||
query := `
|
||||
INSERT INTO chats (user1_id, user2_id, created_at)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id
|
||||
`
|
||||
chat := &domain.Chat{
|
||||
User1ID: user1ID,
|
||||
User2ID: user2ID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
err := r.db.QueryRowContext(ctx, query, user1ID, user2ID, chat.CreatedAt).Scan(&chat.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create chat: %v", err)
|
||||
}
|
||||
return chat, nil
|
||||
}
|
||||
|
||||
func (r *chatRepository) checkUserExists(ctx context.Context, userID int) error {
|
||||
var exists bool
|
||||
err := r.db.QueryRowContext(ctx, "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)", userID).Scan(&exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *chatRepository) GetChatByID(ctx context.Context, id int) (*domain.Chat, error) {
|
||||
query := `
|
||||
SELECT id, user1_id, user2_id, created_at
|
||||
FROM chats
|
||||
WHERE id = $1
|
||||
`
|
||||
chat := &domain.Chat{}
|
||||
err := r.db.QueryRowContext(ctx, query, id).Scan(
|
||||
&chat.ID,
|
||||
&chat.User1ID,
|
||||
&chat.User2ID,
|
||||
&chat.CreatedAt,
|
||||
)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrChatNotFound
|
||||
}
|
||||
return chat, err
|
||||
}
|
||||
|
||||
func (r *chatRepository) GetUserChats(ctx context.Context, userID int) ([]*domain.Chat, error) {
|
||||
query := `
|
||||
SELECT id, user1_id, user2_id, created_at
|
||||
FROM chats
|
||||
WHERE user1_id = $1 OR user2_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
rows, err := r.db.QueryContext(ctx, query, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var chats []*domain.Chat
|
||||
for rows.Next() {
|
||||
var chat domain.Chat
|
||||
if err := rows.Scan(
|
||||
&chat.ID,
|
||||
&chat.User1ID,
|
||||
&chat.User2ID,
|
||||
&chat.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chats = append(chats, &chat)
|
||||
}
|
||||
return chats, nil
|
||||
}
|
||||
|
||||
func (r *chatRepository) GetChatByParticipants(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error) {
|
||||
// Упорядочиваем ID пользователей согласно CHECK constraint
|
||||
if user1ID > user2ID {
|
||||
user1ID, user2ID = user2ID, user1ID
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT id, user1_id, user2_id, created_at
|
||||
FROM chats
|
||||
WHERE user1_id = $1 AND user2_id = $2
|
||||
LIMIT 1
|
||||
`
|
||||
chat := &domain.Chat{}
|
||||
err := r.db.QueryRowContext(ctx, query, user1ID, user2ID).Scan(
|
||||
&chat.ID,
|
||||
&chat.User1ID,
|
||||
&chat.User2ID,
|
||||
&chat.CreatedAt,
|
||||
)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrChatNotFound
|
||||
}
|
||||
return chat, err
|
||||
}
|
||||
func (r *chatRepository) GetUnreadCount(ctx context.Context, chatID, userID int) (int, error) {
|
||||
var count int
|
||||
err := r.db.QueryRowContext(ctx, `
|
||||
SELECT COUNT(*)
|
||||
FROM messages
|
||||
WHERE chat_id = $1
|
||||
AND sender_id != $2
|
||||
AND status = 'sent'
|
||||
`, chatID, userID).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"tailly_back_v2/internal/domain"
|
||||
"tailly_back_v2/internal/repository"
|
||||
"tailly_back_v2/internal/ws"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ChatService interface {
|
||||
SendMessage(ctx context.Context, senderID, chatID int, content string) (*domain.Message, error)
|
||||
GetChatMessages(ctx context.Context, chatID, userID int, limit, offset int) ([]*domain.Message, error)
|
||||
MarkAsRead(ctx context.Context, messageID int) error
|
||||
DeleteMessage(ctx context.Context, messageID, userID int) error
|
||||
GetUserChats(ctx context.Context, userID int) ([]*domain.Chat, error)
|
||||
GetOrCreateChat(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error)
|
||||
GetUnreadCount(ctx context.Context, chatID, userID int) (int, error)
|
||||
GetChatByID(ctx context.Context, id int) (*domain.Chat, error)
|
||||
}
|
||||
|
||||
type chatService struct {
|
||||
chatRepo repository.ChatRepository
|
||||
userRepo repository.UserRepository
|
||||
hub *ws.Hub
|
||||
}
|
||||
|
||||
func NewChatService(chatRepo repository.ChatRepository, userRepo repository.UserRepository, hub *ws.Hub) ChatService {
|
||||
return &chatService{
|
||||
chatRepo: chatRepo,
|
||||
userRepo: userRepo,
|
||||
hub: hub,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *chatService) SendMessage(ctx context.Context, senderID, chatID int, content string) (*domain.Message, error) {
|
||||
chat, err := s.chatRepo.GetChatByID(ctx, chatID)
|
||||
if err != nil {
|
||||
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 всегда установлен
|
||||
Content: content,
|
||||
Status: "sent",
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Сохраняем в БД
|
||||
if err := s.chatRepo.SaveMessage(ctx, message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Отправляем через WebSocket только один раз
|
||||
if s.hub != nil {
|
||||
// Добавляем проверку перед рассылкой
|
||||
if message.ReceiverID == 0 {
|
||||
return nil, errors.New("receiver ID is required")
|
||||
}
|
||||
s.hub.Broadcast(message)
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
func (s *chatService) GetChatMessages(ctx context.Context, chatID, userID int, limit, offset int) ([]*domain.Message, error) {
|
||||
// Проверяем доступ пользователя к чату
|
||||
chat, err := s.chatRepo.GetChatByID(ctx, chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if userID != chat.User1ID && userID != chat.User2ID {
|
||||
return nil, errors.New("access denied")
|
||||
}
|
||||
|
||||
return s.chatRepo.GetMessagesByChat(ctx, chatID, limit, offset)
|
||||
}
|
||||
|
||||
func (s *chatService) MarkAsRead(ctx context.Context, messageID int) error {
|
||||
return s.chatRepo.UpdateMessageStatus(ctx, messageID, "read")
|
||||
}
|
||||
|
||||
func (s *chatService) DeleteMessage(ctx context.Context, messageID, userID int) error {
|
||||
message, err := s.chatRepo.GetMessageByID(ctx, messageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if message.SenderID != userID {
|
||||
return errors.New("only sender can delete the message")
|
||||
}
|
||||
|
||||
return s.chatRepo.DeleteMessage(ctx, messageID)
|
||||
}
|
||||
|
||||
func (s *chatService) GetUserChats(ctx context.Context, userID int) ([]*domain.Chat, error) {
|
||||
return s.chatRepo.GetUserChats(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *chatService) GetOrCreateChat(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error) {
|
||||
// Проверяем существование чата
|
||||
chat, err := s.chatRepo.GetChatByParticipants(ctx, user1ID, user2ID)
|
||||
if err == nil {
|
||||
return chat, nil
|
||||
}
|
||||
|
||||
if !errors.Is(err, repository.ErrChatNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Создаем новый чат
|
||||
return s.chatRepo.CreateChat(ctx, user1ID, user2ID)
|
||||
}
|
||||
func (s *chatService) GetUnreadCount(ctx context.Context, chatID, userID int) (int, error) {
|
||||
return s.chatRepo.GetUnreadCount(ctx, chatID, userID)
|
||||
}
|
||||
func (s *chatService) GetChatByID(ctx context.Context, id int) (*domain.Chat, error) {
|
||||
return s.chatRepo.GetChatByID(ctx, id)
|
||||
}
|
||||
@ -2,7 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
_ "tailly_back_v2/internal/repository"
|
||||
"tailly_back_v2/internal/ws"
|
||||
"tailly_back_v2/proto"
|
||||
)
|
||||
|
||||
type Services struct {
|
||||
@ -15,11 +15,10 @@ type Services struct {
|
||||
Mail MailService
|
||||
Recovery RecoveryService
|
||||
Audit AuditService
|
||||
Chat ChatService
|
||||
ChatHub *ws.Hub
|
||||
Messages proto.MessageServiceClient
|
||||
}
|
||||
|
||||
func NewServices(authService AuthService, userService UserService, postService PostService, commentService CommentService, likeService LikeService, mailService MailService, auditService AuditService, recoveryService RecoveryService, sessionService SessionService, chatService ChatService, chatHub *ws.Hub) *Services {
|
||||
func NewServices(authService AuthService, userService UserService, postService PostService, commentService CommentService, likeService LikeService, mailService MailService, auditService AuditService, recoveryService RecoveryService, sessionService SessionService, messages proto.MessageServiceClient) *Services {
|
||||
return &Services{
|
||||
Auth: authService,
|
||||
User: userService,
|
||||
@ -30,7 +29,6 @@ func NewServices(authService AuthService, userService UserService, postService P
|
||||
Mail: mailService,
|
||||
Recovery: recoveryService,
|
||||
Audit: auditService,
|
||||
Chat: chatService,
|
||||
ChatHub: chatHub,
|
||||
Messages: messages,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,126 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
877
proto/messages.pb.go
Normal file
877
proto/messages.pb.go
Normal file
@ -0,0 +1,877 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.6
|
||||
// protoc v3.21.12
|
||||
// source: messages.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type CreateChatRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
User1Id int32 `protobuf:"varint,1,opt,name=user1_id,json=user1Id,proto3" json:"user1_id,omitempty"`
|
||||
User2Id int32 `protobuf:"varint,2,opt,name=user2_id,json=user2Id,proto3" json:"user2_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CreateChatRequest) Reset() {
|
||||
*x = CreateChatRequest{}
|
||||
mi := &file_messages_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CreateChatRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CreateChatRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CreateChatRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CreateChatRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CreateChatRequest) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *CreateChatRequest) GetUser1Id() int32 {
|
||||
if x != nil {
|
||||
return x.User1Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *CreateChatRequest) GetUser2Id() int32 {
|
||||
if x != nil {
|
||||
return x.User2Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type SendMessageRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ChatId int32 `protobuf:"varint,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
|
||||
SenderId int32 `protobuf:"varint,2,opt,name=sender_id,json=senderId,proto3" json:"sender_id,omitempty"`
|
||||
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SendMessageRequest) Reset() {
|
||||
*x = SendMessageRequest{}
|
||||
mi := &file_messages_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SendMessageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SendMessageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SendMessageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SendMessageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SendMessageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SendMessageRequest) GetChatId() int32 {
|
||||
if x != nil {
|
||||
return x.ChatId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendMessageRequest) GetSenderId() int32 {
|
||||
if x != nil {
|
||||
return x.SenderId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SendMessageRequest) GetContent() string {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetChatRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
User1Id int32 `protobuf:"varint,1,opt,name=user1_id,json=user1Id,proto3" json:"user1_id,omitempty"`
|
||||
User2Id int32 `protobuf:"varint,2,opt,name=user2_id,json=user2Id,proto3" json:"user2_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetChatRequest) Reset() {
|
||||
*x = GetChatRequest{}
|
||||
mi := &file_messages_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetChatRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetChatRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetChatRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetChatRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetChatRequest) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GetChatRequest) GetUser1Id() int32 {
|
||||
if x != nil {
|
||||
return x.User1Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GetChatRequest) GetUser2Id() int32 {
|
||||
if x != nil {
|
||||
return x.User2Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetChatMessagesRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ChatId int32 `protobuf:"varint,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
|
||||
Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetChatMessagesRequest) Reset() {
|
||||
*x = GetChatMessagesRequest{}
|
||||
mi := &file_messages_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetChatMessagesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetChatMessagesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetChatMessagesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetChatMessagesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetChatMessagesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *GetChatMessagesRequest) GetChatId() int32 {
|
||||
if x != nil {
|
||||
return x.ChatId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GetChatMessagesRequest) GetLimit() int32 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GetChatMessagesRequest) GetOffset() int32 {
|
||||
if x != nil {
|
||||
return x.Offset
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetUserChatsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
UserId int32 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetUserChatsRequest) Reset() {
|
||||
*x = GetUserChatsRequest{}
|
||||
mi := &file_messages_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetUserChatsRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetUserChatsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetUserChatsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetUserChatsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetUserChatsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *GetUserChatsRequest) GetUserId() int32 {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type UpdateMessageStatusRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
MessageId int32 `protobuf:"varint,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||
Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UpdateMessageStatusRequest) Reset() {
|
||||
*x = UpdateMessageStatusRequest{}
|
||||
mi := &file_messages_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UpdateMessageStatusRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UpdateMessageStatusRequest) ProtoMessage() {}
|
||||
|
||||
func (x *UpdateMessageStatusRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UpdateMessageStatusRequest.ProtoReflect.Descriptor instead.
|
||||
func (*UpdateMessageStatusRequest) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *UpdateMessageStatusRequest) GetMessageId() int32 {
|
||||
if x != nil {
|
||||
return x.MessageId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *UpdateMessageStatusRequest) GetStatus() string {
|
||||
if x != nil {
|
||||
return x.Status
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type StreamMessagesRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
UserId int32 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *StreamMessagesRequest) Reset() {
|
||||
*x = StreamMessagesRequest{}
|
||||
mi := &file_messages_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *StreamMessagesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StreamMessagesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *StreamMessagesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StreamMessagesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*StreamMessagesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *StreamMessagesRequest) GetUserId() int32 {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
ChatId int32 `protobuf:"varint,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
|
||||
SenderId int32 `protobuf:"varint,3,opt,name=sender_id,json=senderId,proto3" json:"sender_id,omitempty"`
|
||||
Content string `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"`
|
||||
Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
|
||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Message) Reset() {
|
||||
*x = Message{}
|
||||
mi := &file_messages_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Message) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Message) ProtoMessage() {}
|
||||
|
||||
func (x *Message) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Message.ProtoReflect.Descriptor instead.
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *Message) GetId() int32 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Message) GetChatId() int32 {
|
||||
if x != nil {
|
||||
return x.ChatId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Message) GetSenderId() int32 {
|
||||
if x != nil {
|
||||
return x.SenderId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Message) GetContent() string {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Message) GetStatus() string {
|
||||
if x != nil {
|
||||
return x.Status
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Message) GetCreatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
User1Id int32 `protobuf:"varint,2,opt,name=user1_id,json=user1Id,proto3" json:"user1_id,omitempty"`
|
||||
User2Id int32 `protobuf:"varint,3,opt,name=user2_id,json=user2Id,proto3" json:"user2_id,omitempty"`
|
||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
|
||||
LastMessage *Message `protobuf:"bytes,6,opt,name=last_message,json=lastMessage,proto3" json:"last_message,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Chat) Reset() {
|
||||
*x = Chat{}
|
||||
mi := &file_messages_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Chat) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Chat) ProtoMessage() {}
|
||||
|
||||
func (x *Chat) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Chat.ProtoReflect.Descriptor instead.
|
||||
func (*Chat) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *Chat) GetId() int32 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Chat) GetUser1Id() int32 {
|
||||
if x != nil {
|
||||
return x.User1Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Chat) GetUser2Id() int32 {
|
||||
if x != nil {
|
||||
return x.User2Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Chat) GetCreatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Chat) GetUpdatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Chat) GetLastMessage() *Message {
|
||||
if x != nil {
|
||||
return x.LastMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MessageResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Message *Message `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *MessageResponse) Reset() {
|
||||
*x = MessageResponse{}
|
||||
mi := &file_messages_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *MessageResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*MessageResponse) ProtoMessage() {}
|
||||
|
||||
func (x *MessageResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use MessageResponse.ProtoReflect.Descriptor instead.
|
||||
func (*MessageResponse) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *MessageResponse) GetMessage() *Message {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MessagesResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Messages []*Message `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *MessagesResponse) Reset() {
|
||||
*x = MessagesResponse{}
|
||||
mi := &file_messages_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *MessagesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*MessagesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *MessagesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use MessagesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*MessagesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *MessagesResponse) GetMessages() []*Message {
|
||||
if x != nil {
|
||||
return x.Messages
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ChatResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Chat *Chat `protobuf:"bytes,1,opt,name=chat,proto3" json:"chat,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ChatResponse) Reset() {
|
||||
*x = ChatResponse{}
|
||||
mi := &file_messages_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ChatResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ChatResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ChatResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ChatResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ChatResponse) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *ChatResponse) GetChat() *Chat {
|
||||
if x != nil {
|
||||
return x.Chat
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UserChatsResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Chats []*Chat `protobuf:"bytes,1,rep,name=chats,proto3" json:"chats,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UserChatsResponse) Reset() {
|
||||
*x = UserChatsResponse{}
|
||||
mi := &file_messages_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UserChatsResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserChatsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *UserChatsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UserChatsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*UserChatsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *UserChatsResponse) GetChats() []*Chat {
|
||||
if x != nil {
|
||||
return x.Chats
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_messages_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_messages_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x0emessages.proto\x12\x05proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n" +
|
||||
"\x11CreateChatRequest\x12\x19\n" +
|
||||
"\buser1_id\x18\x01 \x01(\x05R\auser1Id\x12\x19\n" +
|
||||
"\buser2_id\x18\x02 \x01(\x05R\auser2Id\"d\n" +
|
||||
"\x12SendMessageRequest\x12\x17\n" +
|
||||
"\achat_id\x18\x01 \x01(\x05R\x06chatId\x12\x1b\n" +
|
||||
"\tsender_id\x18\x02 \x01(\x05R\bsenderId\x12\x18\n" +
|
||||
"\acontent\x18\x03 \x01(\tR\acontent\"F\n" +
|
||||
"\x0eGetChatRequest\x12\x19\n" +
|
||||
"\buser1_id\x18\x01 \x01(\x05R\auser1Id\x12\x19\n" +
|
||||
"\buser2_id\x18\x02 \x01(\x05R\auser2Id\"_\n" +
|
||||
"\x16GetChatMessagesRequest\x12\x17\n" +
|
||||
"\achat_id\x18\x01 \x01(\x05R\x06chatId\x12\x14\n" +
|
||||
"\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x16\n" +
|
||||
"\x06offset\x18\x03 \x01(\x05R\x06offset\".\n" +
|
||||
"\x13GetUserChatsRequest\x12\x17\n" +
|
||||
"\auser_id\x18\x01 \x01(\x05R\x06userId\"S\n" +
|
||||
"\x1aUpdateMessageStatusRequest\x12\x1d\n" +
|
||||
"\n" +
|
||||
"message_id\x18\x01 \x01(\x05R\tmessageId\x12\x16\n" +
|
||||
"\x06status\x18\x02 \x01(\tR\x06status\"0\n" +
|
||||
"\x15StreamMessagesRequest\x12\x17\n" +
|
||||
"\auser_id\x18\x01 \x01(\x05R\x06userId\"\xbc\x01\n" +
|
||||
"\aMessage\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x17\n" +
|
||||
"\achat_id\x18\x02 \x01(\x05R\x06chatId\x12\x1b\n" +
|
||||
"\tsender_id\x18\x03 \x01(\x05R\bsenderId\x12\x18\n" +
|
||||
"\acontent\x18\x04 \x01(\tR\acontent\x12\x16\n" +
|
||||
"\x06status\x18\x05 \x01(\tR\x06status\x129\n" +
|
||||
"\n" +
|
||||
"created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"\xf5\x01\n" +
|
||||
"\x04Chat\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x19\n" +
|
||||
"\buser1_id\x18\x02 \x01(\x05R\auser1Id\x12\x19\n" +
|
||||
"\buser2_id\x18\x03 \x01(\x05R\auser2Id\x129\n" +
|
||||
"\n" +
|
||||
"created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
|
||||
"\n" +
|
||||
"updated_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x121\n" +
|
||||
"\flast_message\x18\x06 \x01(\v2\x0e.proto.MessageR\vlastMessage\";\n" +
|
||||
"\x0fMessageResponse\x12(\n" +
|
||||
"\amessage\x18\x01 \x01(\v2\x0e.proto.MessageR\amessage\">\n" +
|
||||
"\x10MessagesResponse\x12*\n" +
|
||||
"\bmessages\x18\x01 \x03(\v2\x0e.proto.MessageR\bmessages\"/\n" +
|
||||
"\fChatResponse\x12\x1f\n" +
|
||||
"\x04chat\x18\x01 \x01(\v2\v.proto.ChatR\x04chat\"6\n" +
|
||||
"\x11UserChatsResponse\x12!\n" +
|
||||
"\x05chats\x18\x01 \x03(\v2\v.proto.ChatR\x05chats2\xf3\x03\n" +
|
||||
"\x0eMessageService\x12;\n" +
|
||||
"\n" +
|
||||
"CreateChat\x12\x18.proto.CreateChatRequest\x1a\x13.proto.ChatResponse\x12@\n" +
|
||||
"\vSendMessage\x12\x19.proto.SendMessageRequest\x1a\x16.proto.MessageResponse\x125\n" +
|
||||
"\aGetChat\x12\x15.proto.GetChatRequest\x1a\x13.proto.ChatResponse\x12I\n" +
|
||||
"\x0fGetChatMessages\x12\x1d.proto.GetChatMessagesRequest\x1a\x17.proto.MessagesResponse\x12D\n" +
|
||||
"\fGetUserChats\x12\x1a.proto.GetUserChatsRequest\x1a\x18.proto.UserChatsResponse\x12P\n" +
|
||||
"\x13UpdateMessageStatus\x12!.proto.UpdateMessageStatusRequest\x1a\x16.proto.MessageResponse\x12H\n" +
|
||||
"\x0eStreamMessages\x12\x1c.proto.StreamMessagesRequest\x1a\x16.proto.MessageResponse0\x01B\n" +
|
||||
"Z\b./;protob\x06proto3"
|
||||
|
||||
var (
|
||||
file_messages_proto_rawDescOnce sync.Once
|
||||
file_messages_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_messages_proto_rawDescGZIP() []byte {
|
||||
file_messages_proto_rawDescOnce.Do(func() {
|
||||
file_messages_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)))
|
||||
})
|
||||
return file_messages_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_messages_proto_goTypes = []any{
|
||||
(*CreateChatRequest)(nil), // 0: proto.CreateChatRequest
|
||||
(*SendMessageRequest)(nil), // 1: proto.SendMessageRequest
|
||||
(*GetChatRequest)(nil), // 2: proto.GetChatRequest
|
||||
(*GetChatMessagesRequest)(nil), // 3: proto.GetChatMessagesRequest
|
||||
(*GetUserChatsRequest)(nil), // 4: proto.GetUserChatsRequest
|
||||
(*UpdateMessageStatusRequest)(nil), // 5: proto.UpdateMessageStatusRequest
|
||||
(*StreamMessagesRequest)(nil), // 6: proto.StreamMessagesRequest
|
||||
(*Message)(nil), // 7: proto.Message
|
||||
(*Chat)(nil), // 8: proto.Chat
|
||||
(*MessageResponse)(nil), // 9: proto.MessageResponse
|
||||
(*MessagesResponse)(nil), // 10: proto.MessagesResponse
|
||||
(*ChatResponse)(nil), // 11: proto.ChatResponse
|
||||
(*UserChatsResponse)(nil), // 12: proto.UserChatsResponse
|
||||
(*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp
|
||||
}
|
||||
var file_messages_proto_depIdxs = []int32{
|
||||
13, // 0: proto.Message.created_at:type_name -> google.protobuf.Timestamp
|
||||
13, // 1: proto.Chat.created_at:type_name -> google.protobuf.Timestamp
|
||||
13, // 2: proto.Chat.updated_at:type_name -> google.protobuf.Timestamp
|
||||
7, // 3: proto.Chat.last_message:type_name -> proto.Message
|
||||
7, // 4: proto.MessageResponse.message:type_name -> proto.Message
|
||||
7, // 5: proto.MessagesResponse.messages:type_name -> proto.Message
|
||||
8, // 6: proto.ChatResponse.chat:type_name -> proto.Chat
|
||||
8, // 7: proto.UserChatsResponse.chats:type_name -> proto.Chat
|
||||
0, // 8: proto.MessageService.CreateChat:input_type -> proto.CreateChatRequest
|
||||
1, // 9: proto.MessageService.SendMessage:input_type -> proto.SendMessageRequest
|
||||
2, // 10: proto.MessageService.GetChat:input_type -> proto.GetChatRequest
|
||||
3, // 11: proto.MessageService.GetChatMessages:input_type -> proto.GetChatMessagesRequest
|
||||
4, // 12: proto.MessageService.GetUserChats:input_type -> proto.GetUserChatsRequest
|
||||
5, // 13: proto.MessageService.UpdateMessageStatus:input_type -> proto.UpdateMessageStatusRequest
|
||||
6, // 14: proto.MessageService.StreamMessages:input_type -> proto.StreamMessagesRequest
|
||||
11, // 15: proto.MessageService.CreateChat:output_type -> proto.ChatResponse
|
||||
9, // 16: proto.MessageService.SendMessage:output_type -> proto.MessageResponse
|
||||
11, // 17: proto.MessageService.GetChat:output_type -> proto.ChatResponse
|
||||
10, // 18: proto.MessageService.GetChatMessages:output_type -> proto.MessagesResponse
|
||||
12, // 19: proto.MessageService.GetUserChats:output_type -> proto.UserChatsResponse
|
||||
9, // 20: proto.MessageService.UpdateMessageStatus:output_type -> proto.MessageResponse
|
||||
9, // 21: proto.MessageService.StreamMessages:output_type -> proto.MessageResponse
|
||||
15, // [15:22] is the sub-list for method output_type
|
||||
8, // [8:15] is the sub-list for method input_type
|
||||
8, // [8:8] is the sub-list for extension type_name
|
||||
8, // [8:8] is the sub-list for extension extendee
|
||||
0, // [0:8] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_messages_proto_init() }
|
||||
func file_messages_proto_init() {
|
||||
if File_messages_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 13,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_messages_proto_goTypes,
|
||||
DependencyIndexes: file_messages_proto_depIdxs,
|
||||
MessageInfos: file_messages_proto_msgTypes,
|
||||
}.Build()
|
||||
File_messages_proto = out.File
|
||||
file_messages_proto_goTypes = nil
|
||||
file_messages_proto_depIdxs = nil
|
||||
}
|
||||
86
proto/messages.proto
Normal file
86
proto/messages.proto
Normal file
@ -0,0 +1,86 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package proto;
|
||||
|
||||
option go_package = "./;proto";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
service MessageService {
|
||||
rpc CreateChat (CreateChatRequest) returns (ChatResponse);
|
||||
rpc SendMessage (SendMessageRequest) returns (MessageResponse);
|
||||
rpc GetChat (GetChatRequest) returns (ChatResponse);
|
||||
rpc GetChatMessages (GetChatMessagesRequest) returns (MessagesResponse);
|
||||
rpc GetUserChats (GetUserChatsRequest) returns (UserChatsResponse);
|
||||
rpc UpdateMessageStatus (UpdateMessageStatusRequest) returns (MessageResponse);
|
||||
rpc StreamMessages (StreamMessagesRequest) returns (stream MessageResponse);
|
||||
}
|
||||
|
||||
message CreateChatRequest {
|
||||
int32 user1_id = 1;
|
||||
int32 user2_id = 2;
|
||||
}
|
||||
|
||||
message SendMessageRequest {
|
||||
int32 chat_id = 1;
|
||||
int32 sender_id = 2;
|
||||
string content = 3;
|
||||
}
|
||||
|
||||
message GetChatRequest {
|
||||
int32 user1_id = 1;
|
||||
int32 user2_id = 2;
|
||||
}
|
||||
|
||||
message GetChatMessagesRequest {
|
||||
int32 chat_id = 1;
|
||||
int32 limit = 2;
|
||||
int32 offset = 3;
|
||||
}
|
||||
|
||||
message GetUserChatsRequest {
|
||||
int32 user_id = 1;
|
||||
}
|
||||
|
||||
message UpdateMessageStatusRequest {
|
||||
int32 message_id = 1;
|
||||
string status = 2;
|
||||
}
|
||||
|
||||
message StreamMessagesRequest {
|
||||
int32 user_id = 1;
|
||||
}
|
||||
|
||||
message Message {
|
||||
int32 id = 1;
|
||||
int32 chat_id = 2;
|
||||
int32 sender_id = 3;
|
||||
string content = 4;
|
||||
string status = 5;
|
||||
google.protobuf.Timestamp created_at = 6;
|
||||
}
|
||||
|
||||
message Chat {
|
||||
int32 id = 1;
|
||||
int32 user1_id = 2;
|
||||
int32 user2_id = 3;
|
||||
google.protobuf.Timestamp created_at = 4;
|
||||
google.protobuf.Timestamp updated_at = 5;
|
||||
Message last_message = 6;
|
||||
}
|
||||
|
||||
message MessageResponse {
|
||||
Message message = 1;
|
||||
}
|
||||
|
||||
message MessagesResponse {
|
||||
repeated Message messages = 1;
|
||||
}
|
||||
|
||||
message ChatResponse {
|
||||
Chat chat = 1;
|
||||
}
|
||||
|
||||
message UserChatsResponse {
|
||||
repeated Chat chats = 1;
|
||||
}
|
||||
353
proto/messages_grpc.pb.go
Normal file
353
proto/messages_grpc.pb.go
Normal file
@ -0,0 +1,353 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v3.21.12
|
||||
// source: messages.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
MessageService_CreateChat_FullMethodName = "/proto.MessageService/CreateChat"
|
||||
MessageService_SendMessage_FullMethodName = "/proto.MessageService/SendMessage"
|
||||
MessageService_GetChat_FullMethodName = "/proto.MessageService/GetChat"
|
||||
MessageService_GetChatMessages_FullMethodName = "/proto.MessageService/GetChatMessages"
|
||||
MessageService_GetUserChats_FullMethodName = "/proto.MessageService/GetUserChats"
|
||||
MessageService_UpdateMessageStatus_FullMethodName = "/proto.MessageService/UpdateMessageStatus"
|
||||
MessageService_StreamMessages_FullMethodName = "/proto.MessageService/StreamMessages"
|
||||
)
|
||||
|
||||
// MessageServiceClient is the client API for MessageService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MessageServiceClient interface {
|
||||
CreateChat(ctx context.Context, in *CreateChatRequest, opts ...grpc.CallOption) (*ChatResponse, error)
|
||||
SendMessage(ctx context.Context, in *SendMessageRequest, opts ...grpc.CallOption) (*MessageResponse, error)
|
||||
GetChat(ctx context.Context, in *GetChatRequest, opts ...grpc.CallOption) (*ChatResponse, error)
|
||||
GetChatMessages(ctx context.Context, in *GetChatMessagesRequest, opts ...grpc.CallOption) (*MessagesResponse, error)
|
||||
GetUserChats(ctx context.Context, in *GetUserChatsRequest, opts ...grpc.CallOption) (*UserChatsResponse, error)
|
||||
UpdateMessageStatus(ctx context.Context, in *UpdateMessageStatusRequest, opts ...grpc.CallOption) (*MessageResponse, error)
|
||||
StreamMessages(ctx context.Context, in *StreamMessagesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MessageResponse], error)
|
||||
}
|
||||
|
||||
type messageServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMessageServiceClient(cc grpc.ClientConnInterface) MessageServiceClient {
|
||||
return &messageServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *messageServiceClient) CreateChat(ctx context.Context, in *CreateChatRequest, opts ...grpc.CallOption) (*ChatResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ChatResponse)
|
||||
err := c.cc.Invoke(ctx, MessageService_CreateChat_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *messageServiceClient) SendMessage(ctx context.Context, in *SendMessageRequest, opts ...grpc.CallOption) (*MessageResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(MessageResponse)
|
||||
err := c.cc.Invoke(ctx, MessageService_SendMessage_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *messageServiceClient) GetChat(ctx context.Context, in *GetChatRequest, opts ...grpc.CallOption) (*ChatResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ChatResponse)
|
||||
err := c.cc.Invoke(ctx, MessageService_GetChat_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *messageServiceClient) GetChatMessages(ctx context.Context, in *GetChatMessagesRequest, opts ...grpc.CallOption) (*MessagesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(MessagesResponse)
|
||||
err := c.cc.Invoke(ctx, MessageService_GetChatMessages_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *messageServiceClient) GetUserChats(ctx context.Context, in *GetUserChatsRequest, opts ...grpc.CallOption) (*UserChatsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UserChatsResponse)
|
||||
err := c.cc.Invoke(ctx, MessageService_GetUserChats_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *messageServiceClient) UpdateMessageStatus(ctx context.Context, in *UpdateMessageStatusRequest, opts ...grpc.CallOption) (*MessageResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(MessageResponse)
|
||||
err := c.cc.Invoke(ctx, MessageService_UpdateMessageStatus_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *messageServiceClient) StreamMessages(ctx context.Context, in *StreamMessagesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[MessageResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MessageService_ServiceDesc.Streams[0], MessageService_StreamMessages_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[StreamMessagesRequest, MessageResponse]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MessageService_StreamMessagesClient = grpc.ServerStreamingClient[MessageResponse]
|
||||
|
||||
// MessageServiceServer is the server API for MessageService service.
|
||||
// All implementations must embed UnimplementedMessageServiceServer
|
||||
// for forward compatibility.
|
||||
type MessageServiceServer interface {
|
||||
CreateChat(context.Context, *CreateChatRequest) (*ChatResponse, error)
|
||||
SendMessage(context.Context, *SendMessageRequest) (*MessageResponse, error)
|
||||
GetChat(context.Context, *GetChatRequest) (*ChatResponse, error)
|
||||
GetChatMessages(context.Context, *GetChatMessagesRequest) (*MessagesResponse, error)
|
||||
GetUserChats(context.Context, *GetUserChatsRequest) (*UserChatsResponse, error)
|
||||
UpdateMessageStatus(context.Context, *UpdateMessageStatusRequest) (*MessageResponse, error)
|
||||
StreamMessages(*StreamMessagesRequest, grpc.ServerStreamingServer[MessageResponse]) error
|
||||
mustEmbedUnimplementedMessageServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMessageServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMessageServiceServer struct{}
|
||||
|
||||
func (UnimplementedMessageServiceServer) CreateChat(context.Context, *CreateChatRequest) (*ChatResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateChat not implemented")
|
||||
}
|
||||
func (UnimplementedMessageServiceServer) SendMessage(context.Context, *SendMessageRequest) (*MessageResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SendMessage not implemented")
|
||||
}
|
||||
func (UnimplementedMessageServiceServer) GetChat(context.Context, *GetChatRequest) (*ChatResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetChat not implemented")
|
||||
}
|
||||
func (UnimplementedMessageServiceServer) GetChatMessages(context.Context, *GetChatMessagesRequest) (*MessagesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetChatMessages not implemented")
|
||||
}
|
||||
func (UnimplementedMessageServiceServer) GetUserChats(context.Context, *GetUserChatsRequest) (*UserChatsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetUserChats not implemented")
|
||||
}
|
||||
func (UnimplementedMessageServiceServer) UpdateMessageStatus(context.Context, *UpdateMessageStatusRequest) (*MessageResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateMessageStatus not implemented")
|
||||
}
|
||||
func (UnimplementedMessageServiceServer) StreamMessages(*StreamMessagesRequest, grpc.ServerStreamingServer[MessageResponse]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method StreamMessages not implemented")
|
||||
}
|
||||
func (UnimplementedMessageServiceServer) mustEmbedUnimplementedMessageServiceServer() {}
|
||||
func (UnimplementedMessageServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMessageServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MessageServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMessageServiceServer interface {
|
||||
mustEmbedUnimplementedMessageServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMessageServiceServer(s grpc.ServiceRegistrar, srv MessageServiceServer) {
|
||||
// If the following call pancis, it indicates UnimplementedMessageServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MessageService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MessageService_CreateChat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateChatRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MessageServiceServer).CreateChat(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MessageService_CreateChat_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MessageServiceServer).CreateChat(ctx, req.(*CreateChatRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MessageService_SendMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SendMessageRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MessageServiceServer).SendMessage(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MessageService_SendMessage_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MessageServiceServer).SendMessage(ctx, req.(*SendMessageRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MessageService_GetChat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetChatRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MessageServiceServer).GetChat(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MessageService_GetChat_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MessageServiceServer).GetChat(ctx, req.(*GetChatRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MessageService_GetChatMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetChatMessagesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MessageServiceServer).GetChatMessages(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MessageService_GetChatMessages_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MessageServiceServer).GetChatMessages(ctx, req.(*GetChatMessagesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MessageService_GetUserChats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetUserChatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MessageServiceServer).GetUserChats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MessageService_GetUserChats_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MessageServiceServer).GetUserChats(ctx, req.(*GetUserChatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MessageService_UpdateMessageStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateMessageStatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MessageServiceServer).UpdateMessageStatus(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MessageService_UpdateMessageStatus_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MessageServiceServer).UpdateMessageStatus(ctx, req.(*UpdateMessageStatusRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _MessageService_StreamMessages_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(StreamMessagesRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MessageServiceServer).StreamMessages(m, &grpc.GenericServerStream[StreamMessagesRequest, MessageResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MessageService_StreamMessagesServer = grpc.ServerStreamingServer[MessageResponse]
|
||||
|
||||
// MessageService_ServiceDesc is the grpc.ServiceDesc for MessageService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MessageService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.MessageService",
|
||||
HandlerType: (*MessageServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CreateChat",
|
||||
Handler: _MessageService_CreateChat_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SendMessage",
|
||||
Handler: _MessageService_SendMessage_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetChat",
|
||||
Handler: _MessageService_GetChat_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetChatMessages",
|
||||
Handler: _MessageService_GetChatMessages_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetUserChats",
|
||||
Handler: _MessageService_GetUserChats_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateMessageStatus",
|
||||
Handler: _MessageService_UpdateMessageStatus_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "StreamMessages",
|
||||
Handler: _MessageService_StreamMessages_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "messages.proto",
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user