I am batman
This commit is contained in:
224
internal/handlers/chatHandler.go
Normal file
224
internal/handlers/chatHandler.go
Normal file
@@ -0,0 +1,224 @@
|
||||
// // filepath: /home/cody/OpsMastery.v5/internal/handlers/chatHandler.go
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"OpsMastery.v5/internal/database"
|
||||
"OpsMastery.v5/internal/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/websocket/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
clients = make(map[*websocket.Conn]uint) // map connection to user ID
|
||||
broadcast = make(chan Message)
|
||||
mu sync.Mutex
|
||||
chatClients = make(map[uint]map[*websocket.Conn]bool) // chatID -> set of connections
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
SenderID uint `json:"sender_id"`
|
||||
RecipientID uint `json:"recipient_id"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
TicketID *uint `json:"ticket_id,omitempty"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
Users []models.User `json:"users"`
|
||||
Messages []models.ChatMessage `json:"messages"`
|
||||
}
|
||||
|
||||
// WebSocket chat endpoint
|
||||
func ChatWebSocket(c *fiber.Ctx) error {
|
||||
// Upgrade to WebSocket
|
||||
if websocket.IsWebSocketUpgrade(c) {
|
||||
return c.Next()
|
||||
}
|
||||
return fiber.ErrUpgradeRequired
|
||||
}
|
||||
|
||||
func HandleChat(c *websocket.Conn, claims map[string]interface{}) {
|
||||
senderID, _ := claims["userID"].(uint)
|
||||
|
||||
// Register connection
|
||||
if chatClients[0] == nil {
|
||||
chatClients[0] = make(map[*websocket.Conn]bool)
|
||||
}
|
||||
chatClients[0][c] = true
|
||||
defer func() {
|
||||
delete(chatClients[0], c)
|
||||
c.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
var msg struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
if err := c.ReadJSON(&msg); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Save message to DB
|
||||
chatMsg := models.ChatMessage{
|
||||
ChatID: 0,
|
||||
SenderID: senderID,
|
||||
Content: msg.Content,
|
||||
SentAt: time.Now(),
|
||||
}
|
||||
database.DB().Create(&chatMsg)
|
||||
|
||||
// Broadcast to all clients in the chat
|
||||
for client := range chatClients[0] {
|
||||
client.WriteJSON(chatMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast messages to all clients
|
||||
func StartBroadcast() {
|
||||
for {
|
||||
msg := <-broadcast
|
||||
mu.Lock()
|
||||
for conn := range clients {
|
||||
if err := conn.WriteJSON(msg); err != nil {
|
||||
fmt.Println("Error broadcasting:", err)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// GET /api/v1/chats?user1=username1&user2=username2
|
||||
func GetChatHistory(c *fiber.Ctx) error {
|
||||
user1 := c.Query("user1")
|
||||
user2 := c.Query("user2")
|
||||
var messages []models.ChatMessage
|
||||
database.DB().Where(
|
||||
"(sender_id = ? AND recipient_id = ?) OR (sender_id = ? AND recipient_id = ?)",
|
||||
user1, user2, user2, user1,
|
||||
).Order("created_at asc").Find(&messages)
|
||||
return c.JSON(messages) // Returns [] if no messages exist
|
||||
}
|
||||
|
||||
// GET /api/v1/chat_partners?user_id=some_user_id
|
||||
func GetChatPartners(c *fiber.Ctx) error {
|
||||
userID := c.Query("user_id")
|
||||
var partners []models.User
|
||||
database.DB().Raw(`
|
||||
SELECT DISTINCT u.*
|
||||
FROM users u
|
||||
JOIN chat_messages cm
|
||||
ON (cm.sender_id = u.id OR cm.recipient_id = u.id)
|
||||
WHERE (cm.sender_id = ? OR cm.recipient_id = ?) AND u.id != ?
|
||||
`, userID, userID, userID).Scan(&partners)
|
||||
return c.JSON(partners)
|
||||
}
|
||||
|
||||
// DELETE /api/v1/chats/delete?user1=username1&user2=username2
|
||||
func DeleteChatBetweenUsers(c *fiber.Ctx) error {
|
||||
user1 := c.Query("user1")
|
||||
user2 := c.Query("user2")
|
||||
result := database.DB().Where(
|
||||
"(sender_id = ? AND recipient_id = ?) OR (sender_id = ? AND recipient_id = ?)",
|
||||
user1, user2, user2, user1,
|
||||
).Delete(&models.ChatMessage{})
|
||||
if result.Error != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"deleted": result.RowsAffected})
|
||||
}
|
||||
|
||||
// Create a new chat (group or direct)
|
||||
func CreateChat(c *fiber.Ctx) error {
|
||||
var input struct {
|
||||
Name string `json:"name"`
|
||||
UserIDs []uint `json:"user_ids"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
TicketID *uint `json:"ticket_id"`
|
||||
}
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
|
||||
chat := models.Chat{
|
||||
Name: input.Name,
|
||||
IsPrivate: input.IsPrivate,
|
||||
TicketID: input.TicketID,
|
||||
}
|
||||
|
||||
if err := database.DB().Create(&chat).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not create chat"})
|
||||
}
|
||||
|
||||
if len(input.UserIDs) > 0 {
|
||||
var users []models.User
|
||||
if err := database.DB().Where("id IN ?", input.UserIDs).Find(&users).Error; err == nil {
|
||||
database.DB().Model(&chat).Association("Users").Append(users)
|
||||
}
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(chat)
|
||||
}
|
||||
|
||||
// Add users to an existing chat
|
||||
func AddUsersToChat(c *fiber.Ctx) error {
|
||||
chatID := c.Params("chatId")
|
||||
var input struct {
|
||||
UserIDs []uint `json:"user_ids"`
|
||||
}
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
var chat models.Chat
|
||||
if err := database.DB().First(&chat, chatID).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Chat not found"})
|
||||
}
|
||||
var users []models.User
|
||||
if err := database.DB().Where("id IN ?", input.UserIDs).Find(&users).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Users not found"})
|
||||
}
|
||||
database.DB().Model(&chat).Association("Users").Append(users)
|
||||
return c.JSON(fiber.Map{"added": len(users)})
|
||||
}
|
||||
|
||||
// Get all chats for a user (for sidebar)
|
||||
func GetChatsForUser(c *fiber.Ctx) error {
|
||||
userID := c.Params("userId")
|
||||
var chats []models.Chat
|
||||
if err := database.DB().Joins("JOIN chat_users ON chat_users.chat_id = chats.id").
|
||||
Where("chat_users.user_id = ?", userID).
|
||||
Preload("Users").
|
||||
Preload("Messages").
|
||||
Find(&chats).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not fetch chats"})
|
||||
}
|
||||
return c.JSON(chats)
|
||||
}
|
||||
|
||||
// Get all messages for a chat
|
||||
func GetChatMessages(c *fiber.Ctx) error {
|
||||
chatID := c.Params("chatId")
|
||||
var messages []models.ChatMessage
|
||||
if err := database.DB().Where("chat_id = ?", chatID).Order("sent_at asc").Find(&messages).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not fetch messages"})
|
||||
}
|
||||
return c.JSON(messages)
|
||||
}
|
||||
|
||||
// Delete a chat (and its messages)
|
||||
func DeleteChat(c *fiber.Ctx) error {
|
||||
chatID := c.Params("chatId")
|
||||
if err := database.DB().Where("chat_id = ?", chatID).Delete(&models.ChatMessage{}).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not delete messages"})
|
||||
}
|
||||
if err := database.DB().Delete(&models.Chat{}, chatID).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not delete chat"})
|
||||
}
|
||||
return c.JSON(fiber.Map{"deleted": chatID})
|
||||
}
|
||||
Reference in New Issue
Block a user