@@ -0,0 +1 @@
|
||||
project_context.txt
|
||||
+60
-2
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+151
-12
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
+8
-2
@@ -21,13 +21,19 @@
|
||||
}
|
||||
input { padding: 8px; width: 300px; }
|
||||
button { padding: 8px 15px; cursor: pointer; }
|
||||
select { padding: 8px; width: 320px; } /* ← для комнат */
|
||||
#roomsList { margin: 10px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>📱 Shagram Chat</h1>
|
||||
|
||||
<div>
|
||||
<input type="text" id="roomInput" placeholder="Room name" value="general">
|
||||
<!-- ← НОВЫЙ БЛОК С КОМНАТАМИ -->
|
||||
<div id="roomsList">
|
||||
<select id="roomSelect">
|
||||
<option value="general">general</option>
|
||||
</select>
|
||||
<button onclick="loadRooms()">🔄 Refresh</button>
|
||||
<button onclick="connectRoom()">Connect</button>
|
||||
</div>
|
||||
|
||||
|
||||
+63
-6
@@ -1,16 +1,71 @@
|
||||
let ws = null;
|
||||
const messagesDiv = document.getElementById('messages');
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
const roomInput = document.getElementById('roomInput');
|
||||
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 = roomInput.value || 'general';
|
||||
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('Connected to room: ' + room);
|
||||
messagesDiv.innerHTML = '<div class="message">✅ Connected to ' + room + '</div>';
|
||||
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) {
|
||||
@@ -27,7 +82,7 @@ function connectRoom() {
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
messagesDiv.innerHTML += '<div class="meassage">⛔ Disconnected</div>';
|
||||
messagesDiv.innerHTML += '<div class="message">⛔ Disconnected</div>';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,4 +95,6 @@ function sendMessage() {
|
||||
|
||||
ws.send(JSON.stringify({text: text}));
|
||||
messageInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
loadRooms();
|
||||
Reference in New Issue
Block a user