From a83b98eb2b7a5c27b61057ec877191125ab4c813 Mon Sep 17 00:00:00 2001 From: Adam French Date: Thu, 5 Mar 2026 21:43:04 +0000 Subject: [PATCH] Make chat persistent across reboot --- backend/main.go | 1 + backend/models/models.go | 3 +- backend/services/database.go | 1 + backend/services/websocket.go | 63 ++++++++++------------------------- 4 files changed, 21 insertions(+), 47 deletions(-) diff --git a/backend/main.go b/backend/main.go index a4801b5..89d28e1 100644 --- a/backend/main.go +++ b/backend/main.go @@ -38,6 +38,7 @@ func main() { if err != nil { log.Fatal(err) } + services.InitWebSocket(db) // SPOTIFY spotifyAuthState := os.Getenv("SPOTIFY_AUTH_STATE") diff --git a/backend/models/models.go b/backend/models/models.go index 7a6ddb1..306a0b4 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -30,8 +30,7 @@ type Post struct { type Message struct { ID uint `gorm:"primarykey" json:"id"` Content string `json:"text"` - AuthorID uint `json:"-"` - Author *User `gorm:"foreignKey:AuthorID" json:"author"` + AuthorID uint `json:"authorId"` CreatedAt time.Time `json:"createdAt"` DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"` } diff --git a/backend/services/database.go b/backend/services/database.go index 96641a1..1128562 100644 --- a/backend/services/database.go +++ b/backend/services/database.go @@ -37,6 +37,7 @@ func migrateDatabase(db *gorm.DB) error { &models.Activity{}, &models.Favorite{}, &models.Rowing{}, + &models.Message{}, ) if err != nil { return err diff --git a/backend/services/websocket.go b/backend/services/websocket.go index 13633c7..f579702 100644 --- a/backend/services/websocket.go +++ b/backend/services/websocket.go @@ -3,49 +3,15 @@ package services import ( "net/http" "sync" - "time" "adam-french.co.uk/backend/models" + "gorm.io/gorm" "github.com/gorilla/websocket" ) const maxMessages = 50 -type MessageRing struct { - buf []models.Message - next int // index to write next - full bool // have we wrapped at least once -} - -func NewMessageRing(size int) *MessageRing { - return &MessageRing{ - buf: make([]models.Message, size), - } -} - -func (r *MessageRing) Add(m models.Message) { - r.buf[r.next] = m - r.next = (r.next + 1) % len(r.buf) - if r.next == 0 { - r.full = true - } -} - -// Messages returns messages in chronological order (oldest -> newest). -func (r *MessageRing) Messages() []models.Message { - if !r.full { - // buffer not wrapped yet; only use [0:next) - return r.buf[:r.next] - } - - // wrapped: start at next, then to end, then from 0 to next - out := make([]models.Message, len(r.buf)) - copy(out, r.buf[r.next:]) - copy(out[len(r.buf)-r.next:], r.buf[:r.next]) - return out -} - var Upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, @@ -55,27 +21,33 @@ var Upgrader = websocket.Upgrader{ } var ( - clients = make(map[*websocket.Conn]bool) - messages = NewMessageRing(maxMessages) - mu sync.Mutex + clients = make(map[*websocket.Conn]bool) + mu sync.Mutex + wsDB *gorm.DB + nextAuthorID uint ) +func InitWebSocket(database *gorm.DB) { + wsDB = database +} + func HandleWebSocket(conn *websocket.Conn) { defer conn.Close() mu.Lock() clients[conn] = true + nextAuthorID++ + authorID := nextAuthorID - history := messages.Messages() + var history []models.Message + wsDB.Order("created_at ASC").Limit(maxMessages).Find(&history) - // Send existing message history to new client for _, msg := range history { if err := conn.WriteJSON(msg); err != nil { mu.Unlock() return } } - mu.Unlock() for { @@ -84,11 +56,13 @@ func HandleWebSocket(conn *websocket.Conn) { break } - incoming.CreatedAt = time.Now() + incoming.AuthorID = authorID - // Store and broadcast mu.Lock() - messages.Add(incoming) + wsDB.Create(&incoming) + wsDB.Where("id NOT IN (?)", + wsDB.Model(&models.Message{}).Select("id").Order("created_at DESC").Limit(maxMessages), + ).Delete(&models.Message{}) for client := range clients { if err := client.WriteJSON(incoming); err != nil { @@ -99,7 +73,6 @@ func HandleWebSocket(conn *websocket.Conn) { mu.Unlock() } - // Cleanup on disconnect mu.Lock() delete(clients, conn) mu.Unlock()