update
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
@@ -10,21 +13,28 @@ type Channel struct {
|
||||
RoomName string `json:"room_name"`
|
||||
TotalMessages int64 `json:"total_messages"`
|
||||
Messages []Message
|
||||
mu sync.RWMutex
|
||||
Mu sync.RWMutex
|
||||
// Active listeners in specific channel
|
||||
Clients map[*websocket.Conn]bool
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Content string
|
||||
Sender *User
|
||||
Timestamp time.Time
|
||||
Content string `json:"content"`
|
||||
Sender *User `json:"sender"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// Methods for channel
|
||||
func (c *Channel) Send(msg Message) {
|
||||
// Lock write
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
func (c *Channel) Broadcast(ctx context.Context, msg Message) {
|
||||
c.Mu.Lock()
|
||||
defer c.Mu.Unlock()
|
||||
|
||||
// Save to history
|
||||
c.Messages = append(c.Messages, msg)
|
||||
c.TotalMessages++
|
||||
|
||||
// Send to all active WebSocket connections
|
||||
for conn := range c.Clients {
|
||||
payload := []byte(msg.Sender.Username + ": " + msg.Content)
|
||||
_ = conn.Write(ctx, websocket.MessageText, payload)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"chat/internal/models"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SendChatMessage(msg string, user *models.User, channel *models.Channel) error {
|
||||
// Handle empty message
|
||||
if len(msg) < 1 {
|
||||
return fmt.Errorf("message cannot be empty")
|
||||
}
|
||||
|
||||
// Handle long message
|
||||
if len(msg) > 500 {
|
||||
return fmt.Errorf("message too long")
|
||||
}
|
||||
|
||||
// Check if user is banned
|
||||
banned := user.IsBanned
|
||||
if banned {
|
||||
return fmt.Errorf("user: %s, is banned", user.ID)
|
||||
}
|
||||
|
||||
// Send message to channel
|
||||
channel.Send(models.Message{
|
||||
Content: msg,
|
||||
Sender: user,
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1 +1,60 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"chat/internal/cache"
|
||||
"chat/internal/models"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
)
|
||||
|
||||
func HandleChat(w http.ResponseWriter, r *http.Request, cache *cache.Cache) {
|
||||
// Get channel id from Cache
|
||||
roomID := r.URL.Query().Get("room")
|
||||
val, found := cache.Get(roomID)
|
||||
if !found {
|
||||
http.Error(w, "Channel not found", 404)
|
||||
return
|
||||
}
|
||||
ch := val.(*models.Channel)
|
||||
|
||||
// Accept websocket
|
||||
conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close(websocket.StatusNormalClosure, "")
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
// Register client to this channel
|
||||
ch.Mu.RLock()
|
||||
for _, msg := range ch.Messages {
|
||||
_ = wsjson.Write(ctx, conn, msg)
|
||||
}
|
||||
ch.Mu.RUnlock()
|
||||
|
||||
// Register for live updates
|
||||
ch.Mu.Lock()
|
||||
if ch.Clients == nil {
|
||||
ch.Clients = make(map[*websocket.Conn]bool)
|
||||
}
|
||||
ch.Clients[conn] = true
|
||||
ch.Mu.Unlock()
|
||||
|
||||
// Simple read loop
|
||||
for {
|
||||
var msg models.Message
|
||||
err := wsjson.Read(ctx, conn, &msg)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
msg.Timestamp = time.Now()
|
||||
ch.Broadcast(ctx, msg)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user