Some checks failed
Backend API CI / test-unit (push) Failing after 0s
Backend API CI / test-integration (push) Failing after 0s
Frontend CI / test (push) Failing after 0s
Storybook Audit / Build & audit Storybook (push) Failing after 0s
Stream Server CI / test (push) Failing after 0s
- ORDER BY dynamiques : whitelist explicite, fallback created_at DESC - Login/register soumis au rate limiter global - VERSION sync + check CI - Nettoyage références veza-chat-server - Go 1.24 partout (Dockerfile, workflows) - TODO/FIXME/HACK convertis en issues ou résolus
377 lines
10 KiB
Go
377 lines
10 KiB
Go
//go:build ignore
|
|
// +build ignore
|
|
|
|
// NOTE: Disabled (build ignore). Chat server removed; re-enable when chat service is reimplemented.
|
|
|
|
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
|
|
"veza-backend-api/internal/services"
|
|
)
|
|
|
|
// ChatHandlers handles chat-related API endpoints
|
|
type ChatHandlers struct {
|
|
chatService *services.ChatService
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewChatHandlers creates new chat handlers
|
|
func NewChatHandlers(chatService *services.ChatService, logger *zap.Logger) *ChatHandlers {
|
|
return &ChatHandlers{
|
|
chatService: chatService,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// InitChatHandlers initializes chat handlers
|
|
func InitChatHandlers(chatService *services.ChatService, logger *zap.Logger) {
|
|
handlers := NewChatHandlers(chatService, logger)
|
|
|
|
// Store handlers globally for route registration
|
|
ChatHandlersInstance = handlers
|
|
}
|
|
|
|
// ChatHandlersInstance holds the global chat handlers instance
|
|
var ChatHandlersInstance *ChatHandlers
|
|
|
|
// CreateMessage creates a new message in a room
|
|
func (h *ChatHandlers) CreateMessage(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Content string `json:"content" binding:"required"`
|
|
Type services.MessageType `json:"type"`
|
|
ParentID *int64 `json:"parent_id,omitempty"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if req.Type == "" {
|
|
req.Type = services.MessageTypeText
|
|
}
|
|
|
|
message, err := h.chatService.CreateMessage(c.Request.Context(), roomID, userID, req.Content, req.Type, req.ParentID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create message", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create message"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"success": true,
|
|
"message": message,
|
|
})
|
|
}
|
|
|
|
// GetMessages retrieves messages for a room
|
|
func (h *ChatHandlers) GetMessages(c *gin.Context) {
|
|
roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
|
|
return
|
|
}
|
|
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
|
beforeIDStr := c.Query("before_id")
|
|
|
|
var beforeID *int64
|
|
if beforeIDStr != "" {
|
|
if id, err := strconv.ParseInt(beforeIDStr, 10, 64); err == nil {
|
|
beforeID = &id
|
|
}
|
|
}
|
|
|
|
messages, err := h.chatService.GetMessages(c.Request.Context(), roomID, page, limit, beforeID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to get messages", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get messages"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"messages": messages,
|
|
"page": page,
|
|
"limit": limit,
|
|
})
|
|
}
|
|
|
|
// AddReaction adds a reaction to a message
|
|
func (h *ChatHandlers) AddReaction(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"})
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Emoji string `json:"emoji" binding:"required"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
reaction, err := h.chatService.AddReaction(c.Request.Context(), messageID, userID, req.Emoji)
|
|
if err != nil {
|
|
h.logger.Error("Failed to add reaction", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add reaction"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"success": true,
|
|
"reaction": reaction,
|
|
})
|
|
}
|
|
|
|
// RemoveReaction removes a reaction from a message
|
|
func (h *ChatHandlers) RemoveReaction(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"})
|
|
return
|
|
}
|
|
|
|
emoji := c.Param("emoji")
|
|
if emoji == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Emoji is required"})
|
|
return
|
|
}
|
|
|
|
err = h.chatService.RemoveReaction(c.Request.Context(), messageID, userID, emoji)
|
|
if err != nil {
|
|
h.logger.Error("Failed to remove reaction", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to remove reaction"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "Reaction removed",
|
|
})
|
|
}
|
|
|
|
// CreateRoom creates a new chat room
|
|
func (h *ChatHandlers) CreateRoom(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
|
|
var req struct {
|
|
Name string `json:"name" binding:"required"`
|
|
Description string `json:"description"`
|
|
Type services.RoomType `json:"type"`
|
|
IsPrivate bool `json:"is_private"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if req.Type == "" {
|
|
req.Type = services.RoomTypePublic
|
|
}
|
|
|
|
room, err := h.chatService.CreateRoom(c.Request.Context(), req.Name, req.Description, req.Type, req.IsPrivate, userID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create room", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create room"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"success": true,
|
|
"room": room,
|
|
})
|
|
}
|
|
|
|
// GetRooms retrieves available rooms
|
|
func (h *ChatHandlers) GetRooms(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
includePrivate := c.DefaultQuery("include_private", "false") == "true"
|
|
|
|
rooms, err := h.chatService.GetRooms(c.Request.Context(), userID, includePrivate)
|
|
if err != nil {
|
|
h.logger.Error("Failed to get rooms", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get rooms"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"rooms": rooms,
|
|
})
|
|
}
|
|
|
|
// JoinRoom adds a user to a room
|
|
func (h *ChatHandlers) JoinRoom(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
|
|
return
|
|
}
|
|
|
|
err = h.chatService.JoinRoom(c.Request.Context(), roomID, userID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to join room", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to join room"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "Successfully joined room",
|
|
})
|
|
}
|
|
|
|
// LeaveRoom removes a user from a room
|
|
func (h *ChatHandlers) LeaveRoom(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
|
|
return
|
|
}
|
|
|
|
err = h.chatService.LeaveRoom(c.Request.Context(), roomID, userID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to leave room", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to leave room"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "Successfully left room",
|
|
})
|
|
}
|
|
|
|
// CreateDirectMessage creates a DM room between two users
|
|
func (h *ChatHandlers) CreateDirectMessage(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
|
|
var req struct {
|
|
UserID int64 `json:"user_id" binding:"required"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
room, err := h.chatService.CreateDirectMessage(c.Request.Context(), userID, req.UserID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create DM", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create direct message"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"success": true,
|
|
"room": room,
|
|
})
|
|
}
|
|
|
|
// SearchMessages searches for messages in a room
|
|
func (h *ChatHandlers) SearchMessages(c *gin.Context) {
|
|
roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"})
|
|
return
|
|
}
|
|
|
|
query := c.Query("q")
|
|
if query == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Search query is required"})
|
|
return
|
|
}
|
|
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
|
|
|
messages, err := h.chatService.SearchMessages(c.Request.Context(), roomID, query, limit)
|
|
if err != nil {
|
|
h.logger.Error("Failed to search messages", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to search messages"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"messages": messages,
|
|
"query": query,
|
|
"limit": limit,
|
|
})
|
|
}
|
|
|
|
// EditMessage edits an existing message
|
|
func (h *ChatHandlers) EditMessage(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"})
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Content string `json:"content" binding:"required"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
message, err := h.chatService.EditMessage(c.Request.Context(), messageID, userID, req.Content)
|
|
if err != nil {
|
|
h.logger.Error("Failed to edit message", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to edit message"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": message,
|
|
})
|
|
}
|
|
|
|
// DeleteMessage deletes a message
|
|
func (h *ChatHandlers) DeleteMessage(c *gin.Context) {
|
|
userID := c.GetInt64("user_id")
|
|
messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"})
|
|
return
|
|
}
|
|
|
|
err = h.chatService.DeleteMessage(c.Request.Context(), messageID, userID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to delete message", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete message"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"success": true,
|
|
"message": "Message deleted successfully",
|
|
})
|
|
}
|