diff --git a/srcs/backend/dockerfile b/srcs/backend/dockerfile index b1ef9c2..b6f454e 100644 --- a/srcs/backend/dockerfile +++ b/srcs/backend/dockerfile @@ -5,10 +5,6 @@ WORKDIR /app COPY package*.json ./ RUN npm install -RUN npm install dotenv -RUN npm install pg -RUN npm install bcrypt -RUN npm install jsonwebtoken COPY . . diff --git a/srcs/backend/index.js b/srcs/backend/index.js index f005821..9f26624 100644 --- a/srcs/backend/index.js +++ b/srcs/backend/index.js @@ -1,20 +1,38 @@ const express = require('express'); +const http = require('http'); +const cors = require('cors'); +const {Server} = require('socket.io'); const authRouter = require('./routes/auth'); +const chatRouter = require('./routes/global_chat'); const {waitForDb, createTables} = require('./db'); +const setupSocketIO = require('./services/socket'); const app = express(); +const server = http.createServer(app); +const io = new Server(server, +{ + cors: + { + origin: "*", + methods: ["GET", "POST"] + } +}); +app.use(cors()); app.use(express.json()); +setupSocketIO(io); + async function startServer() { await waitForDb(); await createTables(); app.use('/api/auth', authRouter); + app.use('/api/global_chat', chatRouter); app.get('/api', (req, res) => res.send('Backend running')); - app.listen(3001, () => + server.listen(3001, () => { console.log('Server ready and listening'); }); diff --git a/srcs/backend/package.json b/srcs/backend/package.json index 3e66dbe..6f8f7c3 100644 --- a/srcs/backend/package.json +++ b/srcs/backend/package.json @@ -1,6 +1,12 @@ { "dependencies": { - "express": "^4.18.2" + "express": "^4.18.2", + "pg": "^8.11.3", + "bcrypt": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "dotenv": "^17.2.3", + "socket.io": "^4.6.1", + "cors": "^2.8.5" } } \ No newline at end of file diff --git a/srcs/backend/routes/global_chat.js b/srcs/backend/routes/global_chat.js new file mode 100644 index 0000000..76bbc15 --- /dev/null +++ b/srcs/backend/routes/global_chat.js @@ -0,0 +1,20 @@ +const express = require('express'); +const router = express.Router(); +const chatService = require('../services/global_chat'); +const authenticateToken = require('../middleware/auth'); + +router.get('/messages', authenticateToken, async(req, res) => +{ + try + { + const messages = await chatService.getRecentMessages(50); + res.json(messages); + } + catch(err) + { + console.error(err); + res.status(500).json({error: 'Server error'}); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/srcs/backend/services/auth.js b/srcs/backend/services/auth.js index 63d928d..5485d7c 100644 --- a/srcs/backend/services/auth.js +++ b/srcs/backend/services/auth.js @@ -21,7 +21,10 @@ async function login(username, password) const token = jwt.sign ( - {userId: user.id}, + { + userId: user.id, + username: username + }, process.env.JWT_SECRET, {expiresIn: '1h'} ); diff --git a/srcs/backend/services/global_chat.js b/srcs/backend/services/global_chat.js new file mode 100644 index 0000000..8a84ee8 --- /dev/null +++ b/srcs/backend/services/global_chat.js @@ -0,0 +1,31 @@ +const {query} = require('../db'); + +async function saveMessage(userId, content) +{ + const result = await query + ( + 'INSERT INTO messages (sender_id, content) VALUES ($1 ,$2) RETURNING *', + [userId, content] + ); + return (result.rows[0]); +} + +async function getRecentMessages(limit = 50) +{ + const result = await query + ( + `SELECT m.sender_id, m.content, m.created_at, u.username + FROM messages m + JOIN users u ON m.sender_id = u.id + ORDER BY m.created_at DESC + LIMIT $1`, + [limit] + ); + return (result.rows.reverse()); +} + +module.exports = +{ + saveMessage, + getRecentMessages +}; \ No newline at end of file diff --git a/srcs/backend/services/socket.js b/srcs/backend/services/socket.js new file mode 100644 index 0000000..63dcf53 --- /dev/null +++ b/srcs/backend/services/socket.js @@ -0,0 +1,47 @@ +const jwt = require('jsonwebtoken'); +const chatService = require('./global_chat'); + +function setupSocketIO(io) +{ + io.use((socket, next) => + { + const token = socket.handshake.auth.token; + if (!token) + return (next(new Error('Authentication error'))); + + try + { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + socket.user = decoded; + next(); + } + catch(err) + { + next(new Error('Authentication error')); + } + }); + + io.on('connection', (socket) => + { + console.log(`User connected: ${socket.user.username}`); + + socket.join('general-chat'); + socket.on('chat-message', async(data) => + { + const message = await chatService.saveMessage(socket.user.userId, data.content); + io.to('general-chat').emit('chat-message', + { + id:message.id, + username: socket.user.username, + content: message.content, + created_at: message.created_at + }); + }); + socket.on('disconnect', () => + { + console.log(`User disconnected: ${socket.user.username}`); + }); + }); +} + +module.exports = setupSocketIO; \ No newline at end of file