diff --git a/go.mod b/go.mod
index 18ee462..531e062 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module chat
go 1.25.0
require (
+ github.com/coder/websocket v1.8.14 // indirect
github.com/labstack/echo/v5 v5.0.4 // indirect
golang.org/x/time v0.14.0 // indirect
)
diff --git a/go.sum b/go.sum
index aab5e4e..52b5459 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
+github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/labstack/echo/v5 v5.0.4 h1:ll3I/O8BifjMztj9dD1vx/peZQv8cR2CTUdQK6QxGGc=
github.com/labstack/echo/v5 v5.0.4/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..95bdecb
--- /dev/null
+++ b/index.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/internal/models/channel.go b/internal/models/channel.go
index cc320bc..82a165d 100644
--- a/internal/models/channel.go
+++ b/internal/models/channel.go
@@ -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)
+ }
}
diff --git a/internal/repositories/messaging.go b/internal/repositories/messaging.go
index 9d131aa..3f43206 100644
--- a/internal/repositories/messaging.go
+++ b/internal/repositories/messaging.go
@@ -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
-}
diff --git a/internal/web/handlers.go b/internal/web/handlers.go
index efb3895..5a5fbd0 100644
--- a/internal/web/handlers.go
+++ b/internal/web/handlers.go
@@ -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)
+ }
+}
diff --git a/main.go b/main.go
index 06ab7d0..5e43b16 100644
--- a/main.go
+++ b/main.go
@@ -1 +1,34 @@
package main
+
+import (
+ "chat/internal/cache"
+ "chat/internal/models"
+ "chat/internal/web"
+ "net/http"
+ "time"
+
+ "github.com/coder/websocket"
+)
+
+func main() {
+ // Initialize cache
+ cache := cache.NewCache(10 * time.Minute)
+
+ // Pregenerate "General" chat room
+ generalRoom := &models.Channel{
+ ID: "general",
+ RoomName: "General Chat",
+ Clients: make(map[*websocket.Conn]bool),
+ }
+ cache.Set(generalRoom.ID, generalRoom, 24*time.Hour)
+
+ // Define websocket route
+ http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
+ web.HandleChat(w, r, cache)
+ })
+
+ // Serve frontend
+ http.Handle("/", http.FileServer(http.Dir(".")))
+
+ http.ListenAndServe(":8080", nil)
+}