diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e15f080 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +project_context.txt \ No newline at end of file diff --git a/cmd/server/main.go b/cmd/server/main.go index 7ad9a18..dad0f38 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -2,8 +2,10 @@ package main import ( "log" + "net/http" "shagram/internal/api" "shagram/internal/db" + "shagram/internal/models" "shagram/internal/websocket" "github.com/gin-gonic/gin" @@ -16,11 +18,67 @@ func main() { } defer database.Close() hub := websocket.NewHub() + hub.GetOrCreateRoom("general") + hub.GetOrCreateRoom("chat") + hub.GetOrCreateRoom("dev") + router := gin.Default() - router.GET("/ws/:room", api.WebSocketHandler(hub)) + router.GET("/ws/:room", func(c *gin.Context) { + api.WebSocketHandler(hub, database)(c) + }) router.GET("/api/rooms", func(c *gin.Context) { - c.JSON(200, gin.H{"rooms": "TODO"}) + rows, err := database.Query(` + SELECT DISTINCT room_id + FROM messages + ORDER BY room_id`) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + defer rows.Close() + + var rooms []string + for rows.Next() { + var roomID string + err := rows.Scan(&roomID) + if err != nil { + continue + } + rooms = append(rooms, roomID) + } + + if len(rooms) == 0 { + rooms = []string{"general", "chat", "dev"} + } + + c.JSON(200, gin.H{"rooms": rooms}) + }) + router.GET("/api/messages/:room", func(c *gin.Context) { + roomID := c.Param("room") + rows, err := database.Query(` + SELECT id, room_id, user, text, created_at + FROM messages + WHERE room_id = ? + ORDER BY created_at DESC + LIMIT 50`, roomID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + defer rows.Close() + var messages []models.Message + for rows.Next() { + var msg models.Message + err := rows.Scan(&msg.ID, &msg.RoomID, &msg.User, &msg.Text, &msg.CreatedAt) + if err != nil { + log.Printf("Scan error: %v", err) + continue + } + messages = append(messages, msg) + } + + c.JSON(200, gin.H{"messages": messages}) }) router.Static("/static", "./static") router.GET("/", func(c *gin.Context) { diff --git a/internal/api/websocket.go b/internal/api/websocket.go index eefba1c..c3e87cb 100644 --- a/internal/api/websocket.go +++ b/internal/api/websocket.go @@ -1,7 +1,9 @@ package api import ( + "log" "net/http" + "shagram/internal/db" "shagram/internal/websocket" "github.com/gin-gonic/gin" @@ -12,7 +14,7 @@ var upgrader = gws.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, } -func WebSocketHandler(hub *websocket.Hub) gin.HandlerFunc { +func WebSocketHandler(hub *websocket.Hub, database *db.DB) gin.HandlerFunc { return func(c *gin.Context) { roomID := c.Param("room") room := hub.GetOrCreateRoom(roomID) @@ -37,6 +39,13 @@ func WebSocketHandler(hub *websocket.Hub) gin.HandlerFunc { if err != nil { break } + _, err = database.Exec(` + INSERT INTO messages (room_id, user, text) + VALUES (?, ?, ?)`, roomID, "user", msg["text"]) + if err != nil { + log.Printf("Save message error: %v", err) + } + message := []byte(msg["text"]) room.Broadcast(message) } diff --git a/internal/websocket/hub.go b/internal/websocket/hub.go index 375521c..aa36110 100644 --- a/internal/websocket/hub.go +++ b/internal/websocket/hub.go @@ -23,3 +23,11 @@ func (h *Hub) CleanupRoom(roomID string) { delete(h.rooms, roomID) } } + +func (h *Hub) Rooms() []string { + ids := make([]string, 0, len(h.rooms)) + for id := range h.rooms { + ids = append(ids, id) + } + return ids +} diff --git a/project_context.txt b/project_context.txt index 443baeb..3b8241b 100644 --- a/project_context.txt +++ b/project_context.txt @@ -3,8 +3,10 @@ package main import ( "log" + "net/http" "shagram/internal/api" "shagram/internal/db" + "shagram/internal/models" "shagram/internal/websocket" "github.com/gin-gonic/gin" @@ -17,11 +19,67 @@ func main() { } defer database.Close() hub := websocket.NewHub() + hub.GetOrCreateRoom("general") + hub.GetOrCreateRoom("chat") + hub.GetOrCreateRoom("dev") + router := gin.Default() - router.GET("/ws/:room", api.WebSocketHandler(hub)) + router.GET("/ws/:room", func(c *gin.Context) { + api.WebSocketHandler(hub, database)(c) + }) router.GET("/api/rooms", func(c *gin.Context) { - c.JSON(200, gin.H{"rooms": "TODO"}) + rows, err := database.Query(` + SELECT DISTINCT room_id + FROM messages + ORDER BY room_id`) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + defer rows.Close() + + var rooms []string + for rows.Next() { + var roomID string + err := rows.Scan(&roomID) + if err != nil { + continue + } + rooms = append(rooms, roomID) + } + + if len(rooms) == 0 { + rooms = []string{"general", "chat", "dev"} + } + + c.JSON(200, gin.H{"rooms": rooms}) + }) + router.GET("/api/messages/:room", func(c *gin.Context) { + roomID := c.Param("room") + rows, err := database.Query(` + SELECT id, room_id, user, text, created_at + FROM messages + WHERE room_id = ? + ORDER BY created_at DESC + LIMIT 50`, roomID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + defer rows.Close() + var messages []models.Message + for rows.Next() { + var msg models.Message + err := rows.Scan(&msg.ID, &msg.RoomID, &msg.User, &msg.Text, &msg.CreatedAt) + if err != nil { + log.Printf("Scan error: %v", err) + continue + } + messages = append(messages, msg) + } + + c.JSON(200, gin.H{"messages": messages}) }) router.Static("/static", "./static") router.GET("/", func(c *gin.Context) { @@ -161,7 +219,9 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= package api import ( + "log" "net/http" + "shagram/internal/db" "shagram/internal/websocket" "github.com/gin-gonic/gin" @@ -172,7 +232,7 @@ var upgrader = gws.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, } -func WebSocketHandler(hub *websocket.Hub) gin.HandlerFunc { +func WebSocketHandler(hub *websocket.Hub, database *db.DB) gin.HandlerFunc { return func(c *gin.Context) { roomID := c.Param("room") room := hub.GetOrCreateRoom(roomID) @@ -197,6 +257,13 @@ func WebSocketHandler(hub *websocket.Hub) gin.HandlerFunc { if err != nil { break } + _, err = database.Exec(` + INSERT INTO messages (room_id, user, text) + VALUES (?, ?, ?)`, roomID, "user", msg["text"]) + if err != nil { + log.Printf("Save message error: %v", err) + } + message := []byte(msg["text"]) room.Broadcast(message) } @@ -283,6 +350,14 @@ func (h *Hub) CleanupRoom(roomID string) { } } +func (h *Hub) Rooms() []string { + ids := make([]string, 0, len(h.rooms)) + for id := range h.rooms { + ids = append(ids, id) + } + return ids +} + --- internal/websocket/room.go --- package websocket @@ -340,7 +415,7 @@ CREATE TABLE IF NOT EXISTS messages ( INSERT OR IGNORE INTO rooms (id, name) VALUES ('general', 'General'); --- shagram.db --- -SQLite format 3 @ . n] P++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)otablemessagesmessagesCREATE TABLE messages ( +SQLite format 3 @ . n] P++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)otablemessagesmessagesCREATE TABLE messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, room_id TEXT NOT NULL, user TEXT NOT NULL, @@ -352,7 +427,8 @@ SQLite format 3 @ name TEXT NOT NULL ))= indexsqlite_autoindex_rooms_1rooms generalGeneral - general + general _ [5yS- Z / _ $ 3chatuserAAA2026-01-12 21:59:44( 3chatuserdfgsadf2026-01-12 21:59:35' 3devusertest DB2026-01-12 21:58:42, #3chatusersefsefsefes2026-01-12 21:43:38' 3chatusersefesf2026-01-12 21:43:35) 3chatuserdfbzgdfg2026-01-12 21:43:26( 3chatuserkuykfgh2026-01-12 21:42:52- 3generaluserawfawdfwa2026-01-12 21:28:44' 3devuserwadawdw2026-01-12 21:28:40& 3chatuserefawe2026-01-12 21:28:16' 3chatuserawdwad2026-01-12 21:27:58$ 3chatuserawd2026-01-12 21:27:25$ 3chatuseradw2026-01-12 21:27:23" + 3chatuserd2026-01-12 21:27:20# 3chatuserda2026-01-12 21:27:16# 3chatuserku2026-01-12 21:26:58# 3chatuserhi2026-01-12 21:26:43' 3generaluserASD2026-01-12 21:16:37$ 3chatuserASD2026-01-12 21:16:30" 3devuserHI2026-01-12 21:16:08! 3devuser12026-01-12 20:59:23- '3devuserHi from DB! 22026-01-12 20:58:43- '3devuserHi from DB! 12026-01-12 20:58:34 messages --- static/index.html --- @@ -377,13 +453,19 @@ SQLite format 3 @ } input { padding: 8px; width: 300px; } button { padding: 8px 15px; cursor: pointer; } + select { padding: 8px; width: 320px; } /* ← для комнат */ + #roomsList { margin: 10px 0; }