veza/veza-backend-api/internal/websocket/chat/handler.go
senke c7fb240dc3 feat(chat): Sprint 3 -- message handlers, real-time features, permissions
- Implement full MessageHandler dispatch with all 18 incoming message types
- Add handler_messages.go: SendMessage, EditMessage, DeleteMessage with ownership checks
- Add handler_rooms.go: JoinConversation, LeaveConversation
- Add handler_history.go: FetchHistory (cursor-based), SearchMessages (ILIKE), SyncMessages
- Add handler_realtime.go: Typing, MarkAsRead, Delivered, AddReaction, RemoveReaction
- Add handler_calls.go: WebRTC signaling relay (CallOffer/Answer/ICE/Hangup/Reject)
- Add PermissionService: CanRead/CanSend/CanJoin/CanModerate based on room_members
- Add RateLimiter: per-user per-action sliding window (in-memory)
- Wire all dependencies in router.go setupChatWebSocket
2026-02-22 20:43:44 +01:00

103 lines
2.7 KiB
Go

package chat
import (
"context"
"veza-backend-api/internal/repositories"
"veza-backend-api/internal/services"
"go.uber.org/zap"
)
type MessageHandler struct {
msgRepo *repositories.ChatMessageRepository
readRepo *repositories.ReadReceiptRepository
deliveredRepo *repositories.DeliveredStatusRepository
reactionRepo *repositories.ReactionRepository
pubsub *services.ChatPubSubService
permissions *PermissionService
rateLimiter *RateLimiter
hub *Hub
logger *zap.Logger
}
func NewMessageHandler(
hub *Hub,
msgRepo *repositories.ChatMessageRepository,
readRepo *repositories.ReadReceiptRepository,
deliveredRepo *repositories.DeliveredStatusRepository,
reactionRepo *repositories.ReactionRepository,
pubsub *services.ChatPubSubService,
permissions *PermissionService,
rateLimiter *RateLimiter,
logger *zap.Logger,
) *MessageHandler {
if logger == nil {
logger = zap.NewNop()
}
return &MessageHandler{
hub: hub,
msgRepo: msgRepo,
readRepo: readRepo,
deliveredRepo: deliveredRepo,
reactionRepo: reactionRepo,
pubsub: pubsub,
permissions: permissions,
rateLimiter: rateLimiter,
logger: logger,
}
}
func (h *MessageHandler) Dispatch(ctx context.Context, client *Client, msg *IncomingMessage) {
switch msg.Type {
case TypePing:
client.SendJSON(NewPongResponse())
case TypeSendMessage:
h.HandleSendMessage(ctx, client, msg)
case TypeEditMessage:
h.HandleEditMessage(ctx, client, msg)
case TypeDeleteMessage:
h.HandleDeleteMessage(ctx, client, msg)
case TypeJoinConversation:
h.HandleJoinConversation(ctx, client, msg)
case TypeLeaveConversation:
h.HandleLeaveConversation(ctx, client, msg)
case TypeFetchHistory:
h.HandleFetchHistory(ctx, client, msg)
case TypeSearchMessages:
h.HandleSearchMessages(ctx, client, msg)
case TypeSyncMessages:
h.HandleSyncMessages(ctx, client, msg)
case TypeTyping:
h.HandleTyping(ctx, client, msg)
case TypeMarkAsRead:
h.HandleMarkAsRead(ctx, client, msg)
case TypeDelivered:
h.HandleDelivered(ctx, client, msg)
case TypeAddReaction:
h.HandleAddReaction(ctx, client, msg)
case TypeRemoveReaction:
h.HandleRemoveReaction(ctx, client, msg)
case TypeCallOffer:
h.HandleCallOffer(ctx, client, msg)
case TypeCallAnswer:
h.HandleCallAnswer(ctx, client, msg)
case TypeICECandidate:
h.HandleICECandidate(ctx, client, msg)
case TypeCallHangup:
h.HandleCallHangup(ctx, client, msg)
case TypeCallReject:
h.HandleCallReject(ctx, client, msg)
default:
h.logger.Warn("Unknown message type",
zap.String("type", msg.Type),
zap.String("user_id", client.UserID.String()))
client.SendJSON(NewErrorResponse("Unknown message type: " + msg.Type))
}
}