106 lines
3.4 KiB
Go
106 lines
3.4 KiB
Go
package chat_service
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"OpsMastery.v5/internal/utils"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gofiber/websocket/v2"
|
|
)
|
|
|
|
// Message struct definition
|
|
// This struct represents a chat message sent/received via WebSocket.
|
|
// It contains the message content and the sender's identifier (email).
|
|
type Message struct {
|
|
Content string `json:"content"` // The actual message text
|
|
Sender string `json:"sender"` // The sender's email or username
|
|
// Add more fields as needed (e.g., timestamp, chatId, etc.)
|
|
}
|
|
|
|
// clients is a map of active WebSocket connections.
|
|
// The key is the pointer to the websocket.Conn, the value is a boolean (always true).
|
|
// This allows us to keep track of all connected clients for broadcasting messages.
|
|
var clients = make(map[*websocket.Conn]bool)
|
|
|
|
// broadcastMessage sends a given Message to all connected clients.
|
|
// If a client connection fails, it is removed from the clients map.
|
|
func broadcastMessage(msg Message) {
|
|
for client := range clients {
|
|
// Send the message as JSON to the client
|
|
err := client.WriteJSON(msg)
|
|
if err != nil {
|
|
// If sending fails, print error, close connection, and remove client
|
|
fmt.Println("Error sending message:", err)
|
|
client.Close()
|
|
delete(clients, client)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start initializes and runs the chat WebSocket server.
|
|
func Start() {
|
|
// Create a new Fiber app instance
|
|
app := fiber.New()
|
|
|
|
// Serve static files from the ./public directory at /public
|
|
app.Static("/public", "./public")
|
|
|
|
// Middleware for /ws route to validate JWT token before allowing WebSocket upgrade
|
|
app.Use("/ws", func(c *fiber.Ctx) error {
|
|
// Extract token from query parameters
|
|
token := c.Query("token")
|
|
if token == "" {
|
|
// If no token, reject the request
|
|
return c.Status(fiber.StatusUnauthorized).SendString("Missing token")
|
|
}
|
|
// Validate the JWT token
|
|
claims, err := utils.ValidateJWT(token, false)
|
|
if err != nil {
|
|
// If token is invalid, reject the request
|
|
return c.Status(fiber.StatusUnauthorized).SendString("Invalid token")
|
|
}
|
|
// Store the user's email in Fiber's context locals for later use
|
|
c.Locals("userEmail", claims["email"])
|
|
// Proceed to the next handler (WebSocket upgrade)
|
|
return c.Next()
|
|
})
|
|
|
|
// WebSocket endpoint for chat communication
|
|
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
|
|
// Retrieve the user's email from context locals
|
|
userEmail, _ := c.Locals("userEmail").(string)
|
|
// Register the new client connection
|
|
clients[c] = true
|
|
// Ensure client is removed and connection closed when handler exits
|
|
defer func() {
|
|
delete(clients, c)
|
|
c.Close()
|
|
}()
|
|
// Main loop: read messages from this client and broadcast to all clients
|
|
for {
|
|
var msg Message
|
|
// Try to read a JSON message from the client
|
|
err := c.ReadJSON(&msg)
|
|
if err != nil {
|
|
// If JSON parsing fails, try to read a raw message (e.g., plain text)
|
|
_, rawMsg, readErr := c.ReadMessage()
|
|
if readErr != nil {
|
|
// If reading fails, log and break the loop (disconnect client)
|
|
fmt.Println("WebSocket closed:", readErr)
|
|
break
|
|
}
|
|
// Construct a Message from the raw message and set sender
|
|
msg = Message{Content: string(rawMsg), Sender: userEmail}
|
|
} else {
|
|
// If JSON was parsed, set the sender field
|
|
msg.Sender = userEmail
|
|
}
|
|
// Broadcast the received message to all connected clients
|
|
broadcastMessage(msg)
|
|
}
|
|
}))
|
|
|
|
// Start the Fiber server on port 5000
|
|
app.Listen(":5000")
|
|
}
|