veza/veza-backend-api/internal/websocket/chat/handler_calls.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

80 lines
2.6 KiB
Go

package chat
import (
"context"
"encoding/json"
"go.uber.org/zap"
)
func (h *MessageHandler) HandleCallOffer(ctx context.Context, client *Client, msg *IncomingMessage) {
if msg.ConversationID == nil || msg.TargetUserID == nil {
client.SendJSON(NewErrorResponse("conversation_id and target_user_id are required"))
return
}
if msg.SDP == "" {
client.SendJSON(NewErrorResponse("sdp is required"))
return
}
outgoing := NewCallOfferResponse(*msg.ConversationID, client.UserID, msg.SDP, msg.CallType)
data, _ := json.Marshal(outgoing)
h.hub.SendToUser(*msg.TargetUserID, data)
h.logger.Info("Call offer relayed",
zap.String("from", client.UserID.String()),
zap.String("to", msg.TargetUserID.String()),
zap.String("type", msg.CallType))
}
func (h *MessageHandler) HandleCallAnswer(ctx context.Context, client *Client, msg *IncomingMessage) {
if msg.ConversationID == nil || msg.CallerUserID == nil {
client.SendJSON(NewErrorResponse("conversation_id and caller_user_id are required"))
return
}
if msg.SDP == "" {
client.SendJSON(NewErrorResponse("sdp is required"))
return
}
outgoing := NewCallAnswerResponse(*msg.ConversationID, *msg.CallerUserID, client.UserID, msg.SDP)
data, _ := json.Marshal(outgoing)
h.hub.SendToUser(*msg.CallerUserID, data)
}
func (h *MessageHandler) HandleICECandidate(ctx context.Context, client *Client, msg *IncomingMessage) {
if msg.ConversationID == nil || msg.TargetUserID == nil {
client.SendJSON(NewErrorResponse("conversation_id and target_user_id are required"))
return
}
if msg.Candidate == "" {
client.SendJSON(NewErrorResponse("candidate is required"))
return
}
outgoing := NewICECandidateResponse(*msg.ConversationID, client.UserID, msg.Candidate)
data, _ := json.Marshal(outgoing)
h.hub.SendToUser(*msg.TargetUserID, data)
}
func (h *MessageHandler) HandleCallHangup(ctx context.Context, client *Client, msg *IncomingMessage) {
if msg.ConversationID == nil || msg.TargetUserID == nil {
client.SendJSON(NewErrorResponse("conversation_id and target_user_id are required"))
return
}
outgoing := NewCallHangupResponse(*msg.ConversationID, client.UserID)
data, _ := json.Marshal(outgoing)
h.hub.SendToUser(*msg.TargetUserID, data)
}
func (h *MessageHandler) HandleCallReject(ctx context.Context, client *Client, msg *IncomingMessage) {
if msg.ConversationID == nil || msg.CallerUserID == nil {
client.SendJSON(NewErrorResponse("conversation_id and caller_user_id are required"))
return
}
outgoing := NewCallRejectedResponse(*msg.ConversationID, client.UserID)
data, _ := json.Marshal(outgoing)
h.hub.SendToUser(*msg.CallerUserID, data)
}