diff --git a/project_context.txt b/project_context.txt deleted file mode 100644 index 2bd64a1..0000000 --- a/project_context.txt +++ /dev/null @@ -1,595 +0,0 @@ ---- cmd/server/main.go --- -package main - -import ( - "log" - "net/http" - "shagram/internal/api" - "shagram/internal/db" - "shagram/internal/models" - "shagram/internal/websocket" - - "github.com/gin-gonic/gin" -) - -func main() { - database, err := db.NewDB("shagram.db") - if err != nil { - log.Fatal(err) - } - defer database.Close() - hub := websocket.NewHub() - hub.GetOrCreateRoom("general") - hub.GetOrCreateRoom("chat") - hub.GetOrCreateRoom("dev") - - router := gin.Default() - - router.GET("/ws/:room", func(c *gin.Context) { - api.WebSocketHandler(hub, database)(c) - }) - router.GET("/api/rooms", func(c *gin.Context) { - 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) { - c.File("./static/index.html") - }) - router.Run(":8080") -} - ---- go.mod --- -module shagram - -go 1.25.5 - -require ( - github.com/bytedance/sonic v1.14.0 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/gin-gonic/gin v1.11.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect - github.com/gorilla/websocket v1.5.3 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.32 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.54.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect - go.uber.org/mock v0.5.0 // indirect - golang.org/x/arch v0.20.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/mod v0.25.0 // indirect - golang.org/x/net v0.42.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/tools v0.34.0 // indirect - google.golang.org/protobuf v1.36.9 // indirect -) - ---- go.sum --- -github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= -github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= -github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= -golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= - ---- internal/api/websocket.go --- -package api - -import ( - "log" - "net/http" - "shagram/internal/db" - "shagram/internal/websocket" - - "github.com/gin-gonic/gin" - gws "github.com/gorilla/websocket" -) - -var upgrader = gws.Upgrader{ - CheckOrigin: func(r *http.Request) bool { return true }, -} - -func WebSocketHandler(hub *websocket.Hub, database *db.DB) gin.HandlerFunc { - return func(c *gin.Context) { - roomID := c.Param("room") - room := hub.GetOrCreateRoom(roomID) - - conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - return - } - - client := &websocket.Client{Conn: conn, Room: room} - room.Register(client) - - go func() { - defer func() { - room.Unregister(client) - hub.CleanupRoom(roomID) - }() - - for { - var msg map[string]string - err := client.Conn.ReadJSON(&msg) - if err != nil { - break - } - user := msg["user"] - if user == "" { - user = "Anonymous" - } - _, 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) - } - }() - } - -} - ---- internal/db/sqlite.go --- -package db - -import ( - "database/sql" - "os" - - _ "github.com/mattn/go-sqlite3" -) - -type DB struct { - *sql.DB -} - -func NewDB(path string) (*DB, error) { - db, err := sql.Open("sqlite3", path) - if err != nil { - return nil, err - } - - schema, err := os.ReadFile("migrations/schema.sql") - if err != nil { - return nil, err - } - - _, err = db.Exec(string(schema)) - if err != nil { - return nil, err - } - - return &DB{db}, nil -} - ---- internal/models/message.go --- -package models - -import "time" - -type Message struct { - ID int `json:"id"` - RoomID string `json:"room_id"` - User string `json:"user"` - Text string `json:"text"` - CreatedAt time.Time `json:"created_at"` -} - -type Room struct { - ID string `json:"id"` - Name string `json:"name"` -} - ---- internal/websocket/hub.go --- -package websocket - -type Hub struct { - rooms map[string]*Room -} - -func NewHub() *Hub { - return &Hub{ - rooms: make(map[string]*Room), - } -} - -func (h *Hub) GetOrCreateRoom(roomID string) *Room { - if _, ok := h.rooms[roomID]; !ok { - h.rooms[roomID] = NewRoom(roomID) - } - return h.rooms[roomID] -} - -func (h *Hub) CleanupRoom(roomID string) { - room := h.rooms[roomID] - if len(room.clients) == 0 { - 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 -} - ---- internal/websocket/room.go --- -package websocket - -import "github.com/gorilla/websocket" - -type Client struct { - Conn *websocket.Conn - Room *Room -} - -type Room struct { - id string - clients map[*Client]bool -} - -func NewRoom(id string) *Room { - return &Room{ - id: id, - clients: make(map[*Client]bool), - } -} - -func (r *Room) Register(client *Client) { - r.clients[client] = true -} - -func (r *Room) Unregister(client *Client) { - delete(r.clients, client) - client.Conn.Close() -} - -func (r *Room) Broadcast(message []byte) { - for client := range r.clients { - err := client.Conn.WriteMessage(websocket.TextMessage, message) - if err != nil { - r.Unregister(client) - } - } -} - ---- migrations/schema.sql --- -CREATE TABLE IF NOT EXISTS rooms ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS messages ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - room_id TEXT NOT NULL, - user TEXT NOT NULL, - text TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (room_id) REFERENCES rooms (id) -); - -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 ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - room_id TEXT NOT NULL, - user TEXT NOT NULL, - text TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (room_id) REFERENCES rooms (id) -)]tableroomsroomsCREATE TABLE rooms ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL -))=indexsqlite_autoindex_rooms_1rooms generalGeneral - - general  2[5 yS- Z /  _ 2+!3chatЭмильку2026-01-13 23:23:54$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 --- - - - - Shagram Chat - - - -

📱 Shagram Chat

- -
- - - - -
- - -
- -
- - -
- - - - - ---- static/js/chat.js --- -let ws = null; -const messagesDiv = document.getElementById('messages'); -const messageInput = document.getElementById('messageInput'); -const roomSelect = document.getElementById('roomSelect'); - -function loadRooms() { - fetch('/api/rooms') - .then(response => response.json()) - .then(data => { - roomSelect.innerHTML = ''; - data.rooms.forEach(room => { - const option = document.createElement('option'); - option.value = room; - option.textContent = room; - roomSelect.appendChild(option); - }); - }) - .catch(error => { - console.error('Error loading rooms:', error); - }) -} - -function connectRoom() { - const room = roomSelect.value || 'general'; - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - - if (ws) { - ws.close(); - } - - ws = new WebSocket(`${protocol}//localhost:8080/ws/${room}`); - - - - ws.onopen = function() { - console.log('WebSocket connected, loading history for', room); - - fetch(`/api/messages/${room}`) - .then(response => { - console.log('API response:', response.status); - return response.json() - }) - .then(data => { - console.log('History data:', data); - messagesDiv.innerHTML = ''; - const history = data.messages || []; - console.log('history length:', history.length); - if (history.length > 0) { - history.reverse().forEach((msg, index) => { - console.log(`Message ${index}:`, msg) - const msgDiv = document.createElement('div'); - msgDiv.className = 'message'; - msgDiv.textContent = `${msg.user}: ${msg.text}`; - messagesDiv.appendChild(msgDiv); - }) - } - const connectMsg = document.createElement('div'); - connectMsg.className = 'message'; - connectMsg.textContent = `✅ Connected to ${room}`; - messagesDiv.appendChild(connectMsg); - messagesDiv.scrollTop = messagesDiv.scrollHeight; - console.log('History loaded!'); - }) - .catch(error => { - console.error('❌ History error', error); - }) - - - }; - - ws.onmessage = function(event) { - const msgDiv = document.createElement('div'); - msgDiv.className = 'message'; - msgDiv.textContent = event.data; - messagesDiv.appendChild(msgDiv); - messagesDiv.scrollTop = messagesDiv.scrollHeight; - }; - - ws.onerror = function(error) { - console.error('WebSocket error:', error); - messagesDiv.innerHTML += '
❌ Error: ' + error + '
'; - }; - - ws.onclose = function() { - messagesDiv.innerHTML += '
⛔ Disconnected
'; - }; -} - -function sendMessage() { - const text = messageInput.value.trim(); - const usernameInput = document.getElementById('userInput'); - const username = usernameInput ? usernameInput.value.trim() || 'Anonymous' : 'Anonymous'; - - if (!text || !ws || ws.readyState !== WebSocket.OPEN) { - alert('Not connected or empty message'); - return; - } - - ws.send(JSON.stringify({ - text: text, - user: username, - })); - messageInput.value = ''; -} - -loadRooms();