add login on front.
This commit is contained in:
@@ -5,6 +5,7 @@ services:
|
|||||||
context: ../..
|
context: ../..
|
||||||
container_name: shagram-app
|
container_name: shagram-app
|
||||||
environment:
|
environment:
|
||||||
|
- WS_ALLOWED_ORIGINS=https://localhost,http://localhost
|
||||||
- DATABASE_PATH=/app/data/shagram.db
|
- DATABASE_PATH=/app/data/shagram.db
|
||||||
- JWT_SECRET=${JWT_SECRET}
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
+33
-11
@@ -3,15 +3,30 @@ package api
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"shagram/internal/auth"
|
||||||
"shagram/internal/db"
|
"shagram/internal/db"
|
||||||
"shagram/internal/websocket"
|
"shagram/internal/websocket"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
gws "github.com/gorilla/websocket"
|
gws "github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
var upgrader = gws.Upgrader{
|
var upgrader = gws.Upgrader{
|
||||||
CheckOrigin: func(r *http.Request) bool { return true },
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
origin := r.Header.Get("Origin")
|
||||||
|
if origin == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
allowed := strings.Split(os.Getenv("WS_ALLOWED_ORIGINS"), ",")
|
||||||
|
for _, a := range allowed {
|
||||||
|
if strings.TrimSpace(a) == origin {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func WebSocketHandler(hub *websocket.Hub, database *db.DB) gin.HandlerFunc {
|
func WebSocketHandler(hub *websocket.Hub, database *db.DB) gin.HandlerFunc {
|
||||||
@@ -19,6 +34,14 @@ func WebSocketHandler(hub *websocket.Hub, database *db.DB) gin.HandlerFunc {
|
|||||||
roomID := c.Param("room")
|
roomID := c.Param("room")
|
||||||
room := hub.GetOrCreateRoom(roomID)
|
room := hub.GetOrCreateRoom(roomID)
|
||||||
|
|
||||||
|
tokenString := c.Query("token")
|
||||||
|
claims, err := auth.ParseAccessToken(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
username := claims.Username
|
||||||
|
|
||||||
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -35,25 +58,24 @@ func WebSocketHandler(hub *websocket.Hub, database *db.DB) gin.HandlerFunc {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
var msg map[string]string
|
var msg map[string]string
|
||||||
err := client.Conn.ReadJSON(&msg)
|
if err := client.Conn.ReadJSON(&msg); err != nil {
|
||||||
if err != nil {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
user := msg["user"]
|
|
||||||
if user == "" {
|
text := msg["text"]
|
||||||
user = "Anonymous"
|
if text == "" {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = database.Exec(`
|
_, err = database.Exec(`
|
||||||
INSERT INTO messages (room_id, user, text)
|
INSERT INTO messages (room_id, user, text)
|
||||||
VALUES (?, ?, ?)`, roomID, user, msg["text"])
|
VALUES (?, ?, ?)`, roomID, username, text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Save message error: %v", err)
|
log.Printf("Save message error: %v", err)
|
||||||
}
|
}
|
||||||
formattedMsg := user + ": " + msg["text"]
|
|
||||||
message := []byte(formattedMsg)
|
room.Broadcast([]byte(username + ": " + text))
|
||||||
room.Broadcast(message)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -23,6 +23,7 @@
|
|||||||
button { padding: 8px 15px; cursor: pointer; }
|
button { padding: 8px 15px; cursor: pointer; }
|
||||||
select { padding: 8px; width: 320px; }
|
select { padding: 8px; width: 320px; }
|
||||||
#roomsList { margin: 10px 0; }
|
#roomsList { margin: 10px 0; }
|
||||||
|
#authStatus { margin-left: 10px; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -32,12 +33,15 @@
|
|||||||
<select id="roomSelect">
|
<select id="roomSelect">
|
||||||
<option value="general">general</option>
|
<option value="general">general</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<input type="text" id="userInput" placeholder="Your name" value="Эмиль" maxlength="20">
|
<input type="text" id="userInput" placeholder="Your name" value="Эмиль" maxlength="20">
|
||||||
|
<button onclick="login()">Login</button>
|
||||||
|
<span id="authStatus"></span>
|
||||||
|
|
||||||
<button onclick="loadRooms()">🔄</button>
|
<button onclick="loadRooms()">🔄</button>
|
||||||
<button onclick="connectRoom()">Connect</button>
|
<button onclick="connectRoom()">Connect</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="messages"></div>
|
<div id="messages"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
+64
-37
@@ -1,71 +1,103 @@
|
|||||||
let ws = null;
|
let ws = null;
|
||||||
|
let accessToken = null;
|
||||||
|
|
||||||
const messagesDiv = document.getElementById('messages');
|
const messagesDiv = document.getElementById('messages');
|
||||||
const messageInput = document.getElementById('messageInput');
|
const messageInput = document.getElementById('messageInput');
|
||||||
const roomSelect = document.getElementById('roomSelect');
|
const roomSelect = document.getElementById('roomSelect');
|
||||||
|
const authStatus = document.getElementById('authStatus');
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
const usernameInput = document.getElementById('userInput');
|
||||||
|
const username = usernameInput ? usernameInput.value.trim() : '';
|
||||||
|
|
||||||
|
if (!username) {
|
||||||
|
alert('Enter username');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
authStatus.textContent = 'Logging in...';
|
||||||
|
|
||||||
|
const resp = await fetch('/api/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ username }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resp.ok) {
|
||||||
|
const text = await resp.text();
|
||||||
|
authStatus.textContent = 'Login failed';
|
||||||
|
console.error('Login failed:', resp.status, text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await resp.json();
|
||||||
|
accessToken = data.access_token || null;
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
authStatus.textContent = 'Login failed';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
authStatus.textContent = '✅ Logged in';
|
||||||
|
}
|
||||||
|
|
||||||
function loadRooms() {
|
function loadRooms() {
|
||||||
fetch('/api/rooms')
|
fetch('/api/rooms')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
roomSelect.innerHTML = '';
|
roomSelect.innerHTML = '';
|
||||||
data.rooms.forEach(room => {
|
(data.rooms || []).forEach(room => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = room;
|
option.value = room;
|
||||||
option.textContent = room;
|
option.textContent = room;
|
||||||
roomSelect.appendChild(option);
|
roomSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error loading rooms:', error);
|
||||||
});
|
});
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error loading rooms:', error);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectRoom() {
|
function connectRoom() {
|
||||||
const room = roomSelect.value || 'general';
|
const room = roomSelect.value || 'general';
|
||||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const host = window.location.host;
|
const host = window.location.host;
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
alert('Please login first');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ws) {
|
if (ws) {
|
||||||
ws.close();
|
ws.close();
|
||||||
}
|
}
|
||||||
const wsUrl = `${protocol}//${host}/ws/${room}`;
|
|
||||||
|
const wsUrl = `${protocol}//${host}/ws/${room}?token=${encodeURIComponent(accessToken)}`;
|
||||||
ws = new WebSocket(wsUrl);
|
ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ws.onopen = function() {
|
ws.onopen = function() {
|
||||||
console.log('WebSocket connected, loading history for', room);
|
|
||||||
|
|
||||||
fetch(`/api/messages/${room}`)
|
fetch(`/api/messages/${room}`)
|
||||||
.then(response => {
|
.then(response => response.json())
|
||||||
console.log('API response:', response.status);
|
|
||||||
return response.json()
|
|
||||||
})
|
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log('History data:', data);
|
|
||||||
messagesDiv.innerHTML = '';
|
messagesDiv.innerHTML = '';
|
||||||
const history = data.messages || [];
|
const history = data.messages || [];
|
||||||
console.log('history length:', history.length);
|
|
||||||
if (history.length > 0) {
|
if (history.length > 0) {
|
||||||
history.reverse().forEach((msg, index) => {
|
history.reverse().forEach(msg => {
|
||||||
console.log(`Message ${index}:`, msg)
|
|
||||||
const msgDiv = document.createElement('div');
|
const msgDiv = document.createElement('div');
|
||||||
msgDiv.className = 'message';
|
msgDiv.className = 'message';
|
||||||
msgDiv.textContent = `${msg.user}: ${msg.text}`;
|
msgDiv.textContent = `${msg.user}: ${msg.text}`;
|
||||||
messagesDiv.appendChild(msgDiv);
|
messagesDiv.appendChild(msgDiv);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
const connectMsg = document.createElement('div');
|
const connectMsg = document.createElement('div');
|
||||||
connectMsg.className = 'message';
|
connectMsg.className = 'message';
|
||||||
connectMsg.textContent = `✅ Connected to ${room}`;
|
connectMsg.textContent = `✅ Connected to ${room}`;
|
||||||
messagesDiv.appendChild(connectMsg);
|
messagesDiv.appendChild(connectMsg);
|
||||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||||
console.log('History loaded!');
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('❌ History error', error);
|
console.error('History error', error);
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
@@ -88,18 +120,13 @@ function connectRoom() {
|
|||||||
|
|
||||||
function sendMessage() {
|
function sendMessage() {
|
||||||
const text = messageInput.value.trim();
|
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) {
|
if (!text || !ws || ws.readyState !== WebSocket.OPEN) {
|
||||||
alert('Not connected or empty message');
|
alert('Not connected or empty message');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({ text }));
|
||||||
text: text,
|
|
||||||
user: username,
|
|
||||||
}));
|
|
||||||
messageInput.value = '';
|
messageInput.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user