From f6a50748a8039a8e5f855d2d60e6ad17386c293a Mon Sep 17 00:00:00 2001 From: slawk0 Date: Sat, 7 Sep 2024 15:21:09 +0200 Subject: [PATCH] code refactor, added comments, removed no necceserry console logs, added settings, fixed app crash on empty password or username POSTed directly to api --- backend/socket.js | 35 +++------- frontend/js/chat.js | 27 +++----- frontend/js/settings.js | 18 +++++ frontend/routes/chat.html | 15 +++-- frontend/routes/settings.html | 17 +++++ frontend/stylesheet/chat.css | 12 ++-- index.js | 123 +++++++++++++++++++++------------- 7 files changed, 147 insertions(+), 100 deletions(-) create mode 100644 frontend/js/settings.js create mode 100644 frontend/routes/settings.html diff --git a/backend/socket.js b/backend/socket.js index 43a1663..4170bee 100644 --- a/backend/socket.js +++ b/backend/socket.js @@ -13,6 +13,7 @@ function initializeSocket(server) { }); io.use((socket, next) => { + // user auth const token = socket.handshake.auth.token; if(token) { jwt.verify(token, jwtSecret, (err, user) => { @@ -28,21 +29,21 @@ function initializeSocket(server) { } }); + // open main socket connection io.on('connection', (socket) => { if (!socket.user) { + console.log(socket.user); console.log('User not authenticated'); + socket.emit('socket error', 'User not authenticated') socket.disconnect(); return; } const username = socket.user.username; - // useless option for log in users activity but why - //console.log(username + ' connected'); // Join a room with the user's username socket.join(username); - // chat message event socket.on('chat message', async (msgData) => { const { content, recipient } = msgData; @@ -51,8 +52,10 @@ function initializeSocket(server) { // Insert the new message into the database const result = await db.query('INSERT INTO messages (content, username, recipient) VALUES ($1, $2, $3) RETURNING id', [content, username, recipient]); insertedId = result.rows[0].id; + } catch (err) { console.error('Error inserting message:', err); + socket.emit('socket error', "Error inserting message, try refreshing the page") return; } @@ -63,7 +66,6 @@ function initializeSocket(server) { if (result.rows.length > 0) { const newMessage = result.rows[0]; - //console.log(formattedMessage); // Emit message to the sender's and recipient's rooms io.to(username).to(recipient).emit('chat message', { username: newMessage.username, recipient: newMessage.recipient, content: newMessage.content }); @@ -71,24 +73,6 @@ function initializeSocket(server) { } catch (err) { console.error('Error fetching inserted message:', err); } - - // I think this is not needed? (it cause duplicate messages with loading messages history) - // if (!socket.recovered) { - // try { - // const query = 'SELECT id, content, username, recipient FROM messages WHERE id > $1 ORDER BY id ASC'; - // const values = [socket.handshake.auth.serverOffset || 0]; - // const result = await db.query(query, values); - // //const newMessage = result.rows[0]; - // for (const row of result.rows) { - // if (row.username === username || row.recipient === username) { - // io.to(username).to(recipient).emit('chat message', { username: row.username, recipient: row.recipient, content: row.content }); - // - // } - // } - // } catch (e) { - // console.error('Error retrieving messages:', e); - // } - // } }); socket.on('get messages', async (recipient) => { const username = socket.user.username; @@ -108,17 +92,16 @@ function initializeSocket(server) { //console.log('Sending historical messages'); socket.emit('messages history', result.rows); } else { - io.emit('no messages', ); + io.emit('no messages'); } } catch (e) { console.error('Error retrieving messages:', e); + socket.emit('socket error', "Error retrieving messages, refresh the page") } }); - - // disconnect event socket.on('disconnect', () => { - console.log(username + ' has disconnected'); + return "Disconnected"; }); }); diff --git a/frontend/js/chat.js b/frontend/js/chat.js index c0c24b4..b40abb4 100644 --- a/frontend/js/chat.js +++ b/frontend/js/chat.js @@ -3,7 +3,7 @@ const input = document.getElementById('input'); const recipientForm = document.getElementById('recipientForm'); const recipientInput = document.getElementById('recipient'); const messages = document.getElementById('messages'); -const logoutButton = document.getElementById('logout'); +const error = document.getElementById('error'); document.getElementById('input').placeholder = `Send message as: ${localStorage.getItem('username')}`; let currentRecipient = null; @@ -11,23 +11,7 @@ window.onload = () => { document.getElementById('recipient').focus(); } - -logoutButton.onclick = logout; -function logout() { - fetch('/auth/logout', { - method: 'POST', - credentials: 'include' - }) - .then(response => { - if (response.ok) { - localStorage.clear(); - window.location.href = '/login'; - } - }) - .catch(error => { - console.error('Logout failed:', error); - }); -} +1 function initializeRecipient() { const savedRecipient = localStorage.getItem('currentRecipient'); if (savedRecipient) { @@ -71,7 +55,7 @@ async function initializeSocket() { socket.emit('get messages', initialRecipient); }); - socket.on('chat message', (msg, serverOffset) => { + socket.on('chat message', (msg) => { console.log('Received message:', msg); const { username, content, recipient } = msg; @@ -88,6 +72,11 @@ async function initializeSocket() { console.log('No previous messages found'); messages.innerHTML = '

No messages found

'; }); + socket.on('socket error', (msg) => { + console.log('There was an error: ', msg) + error.innerHTML = msg; + messages.innerHTML = '

No messages found

'; + }) recipientForm.addEventListener('submit', (e) => { e.preventDefault(); if (recipientInput.value) { diff --git a/frontend/js/settings.js b/frontend/js/settings.js new file mode 100644 index 0000000..380ec43 --- /dev/null +++ b/frontend/js/settings.js @@ -0,0 +1,18 @@ +const logoutButton = document.getElementById('logout'); + +logoutButton.onclick = logout; +function logout() { + fetch('/auth/logout', { + method: 'POST', + credentials: 'include' + }) + .then(response => { + if (response.ok) { + localStorage.clear(); + window.location.href = '/login'; + } + }) + .catch(error => { + console.error('Logout failed:', error); + }); +} diff --git a/frontend/routes/chat.html b/frontend/routes/chat.html index 526179b..01f82d8 100644 --- a/frontend/routes/chat.html +++ b/frontend/routes/chat.html @@ -1,25 +1,32 @@ - + - + + + Chat - + +
+
+

- +
+ \ No newline at end of file diff --git a/frontend/routes/settings.html b/frontend/routes/settings.html new file mode 100644 index 0000000..fff0949 --- /dev/null +++ b/frontend/routes/settings.html @@ -0,0 +1,17 @@ + + + + + + + Settings + + + + +

SETTINGS

+ + + + \ No newline at end of file diff --git a/frontend/stylesheet/chat.css b/frontend/stylesheet/chat.css index de7bad8..9000138 100644 --- a/frontend/stylesheet/chat.css +++ b/frontend/stylesheet/chat.css @@ -89,14 +89,16 @@ button:hover { background-color: #155bb5; } -#logout { - background-color: #d32f2f; +#settings { + background-color: #1a73e8; } -#logout:hover { - background-color: #b71c1c; +#settings:hover { + background-color: #1a73e8; +} +#error { + color: red; } - #messages { font-weight: lighter; list-style-type: none; diff --git a/index.js b/index.js index ab5b7d8..67b9cb7 100644 --- a/index.js +++ b/index.js @@ -44,17 +44,17 @@ app.post('/auth/signup', async (req, res) => { }); // logout API - app.post('/auth/logout', (req, res) => { - // clear JWT token - res.clearCookie('token', { - path: '/' - }); - // clear socket.io cookie (no idea what is it for) - res.clearCookie('io', { - path: '/' - }); - res.status(200).json({ message: 'Successfully logged out'}); - }) +app.post('/auth/logout', (req, res) => { + // clear JWT token + res.clearCookie('token', { + path: '/' + }); + // clear socket.io cookie (no idea what is it for but better remove it) + res.clearCookie('io', { + path: '/' + }); + res.status(200).json({ message: 'Successfully logged out'}); +}); // get JWT token API app.get('/auth/token', (req, res) => { @@ -63,7 +63,25 @@ app.get('/auth/token', (req, res) => { res.send('Not logged in'); } res.send(token); -}) +}); + +app.post('/auth/changepassword', (req, res) => { + +}); +// get username +app.get('/auth/user', (req, res) => { + const token = req.cookies.token; + if(token) { + jwt.verify(token, jwtSecret, (err, user) => { + if(err) { + return res.status(403).send('Unauthorized'); + } else { + const username = user.username; + res.json({username}); + } + }); + } +}); // serving the login page app.get('/login', (req, res) => { @@ -79,25 +97,27 @@ app.get('/login', (req, res) => { app.get('/signup', (req, res) => { const token = req.cookies.token; if(token){ - res.json({Error: 'Already logged in'}); + res.json({ Error: 'Already logged in' }); } else res.sendFile(path.join(__dirname, '/frontend/routes/signup.html')); }); - -app.get('/auth/user', (req, res) => { +app.get('/settings', (req, res) => { const token = req.cookies.token; - if(token) { - jwt.verify(token, jwtSecret, (err, user) => { - if(err) { - return res.status(403).send('Unauthorized'); - } else { - const username = user.username; - res.json({username}); - } - }); + + if(!token) { + res.redirect('/login'); + return; } -}); + + // verify token + jwt.verify(token, jwtSecret, (err) => { + if (err) { + return res.status(403).send('Unauthorized'); + } + res.sendFile(path.join(__dirname, '/frontend/routes/settings.html')) + }); +}) // serving the chat page if logged in app.get('/', (req, res) => { @@ -124,37 +144,48 @@ server.listen(port, () => { // signup function async function signupUser(req, res) { - let username = req.body.username.trim(); - let password = req.body.password.trim(); + let username = req.body.username; + let password = req.body.password; - try { - // Check if user exists - const exists = await isUserExists(username); - if (exists) { - console.log('User already exists'); - return res.status(500).send('User already exists!'); + if(username && password){ + try { + // trimming here to avoid app crash when username or password is undefined + username = username.trim(); + password = password.trim(); + // Check if user exists + const exists = await isUserExists(username); + if (exists) { + console.log('User already exists'); + return res.status(500).send('User already exists!'); + } + + // Hash password + const salt = await bcrypt.genSalt(saltRounds); + const hash = await bcrypt.hash(password, salt); + + // Insert user + await insertUser(username, hash); + return res.status(200).send("Account successfully created Login screen"); + } catch (err) { + console.error('Error inserting data:', err); + return res.status(500).send('Error inserting data'); } - - // Hash password - const salt = await bcrypt.genSalt(saltRounds); - const hash = await bcrypt.hash(password, salt); - - // Insert user - await insertUser(username, hash); - return res.status(200).send("Account successfully created Login screen"); - } catch (err) { - console.error('Error inserting data:', err); - return res.status(500).send('Error inserting data'); + } else { + res.send('Please enter Username and Password!') } } // login function async function loginUser(req, res) { - let username = req.body.username.trim(); - let password = req.body.password.trim(); + + let username = req.body.username; + let password = req.body.password; if (username && password) { try { + username = username.trim(); + password = password.trim(); + const result = await db.query('SELECT * FROM accounts WHERE username = $1', [username]); // check if user exists if (result.rows.length > 0) {