code refactor, added comments, removed no necceserry console logs, added settings, fixed app crash on empty password or username POSTed directly to api
This commit is contained in:
@@ -13,6 +13,7 @@ function initializeSocket(server) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
io.use((socket, next) => {
|
io.use((socket, next) => {
|
||||||
|
// user auth
|
||||||
const token = socket.handshake.auth.token;
|
const token = socket.handshake.auth.token;
|
||||||
if(token) {
|
if(token) {
|
||||||
jwt.verify(token, jwtSecret, (err, user) => {
|
jwt.verify(token, jwtSecret, (err, user) => {
|
||||||
@@ -28,21 +29,21 @@ function initializeSocket(server) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// open main socket connection
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
|
|
||||||
if (!socket.user) {
|
if (!socket.user) {
|
||||||
|
console.log(socket.user);
|
||||||
console.log('User not authenticated');
|
console.log('User not authenticated');
|
||||||
|
socket.emit('socket error', 'User not authenticated')
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const username = socket.user.username;
|
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
|
// Join a room with the user's username
|
||||||
socket.join(username);
|
socket.join(username);
|
||||||
|
|
||||||
|
|
||||||
// chat message event
|
// chat message event
|
||||||
socket.on('chat message', async (msgData) => {
|
socket.on('chat message', async (msgData) => {
|
||||||
const { content, recipient } = msgData;
|
const { content, recipient } = msgData;
|
||||||
@@ -51,8 +52,10 @@ function initializeSocket(server) {
|
|||||||
// Insert the new message into the database
|
// 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]);
|
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;
|
insertedId = result.rows[0].id;
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error inserting message:', err);
|
console.error('Error inserting message:', err);
|
||||||
|
socket.emit('socket error', "Error inserting message, try refreshing the page")
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +66,6 @@ function initializeSocket(server) {
|
|||||||
|
|
||||||
if (result.rows.length > 0) {
|
if (result.rows.length > 0) {
|
||||||
const newMessage = result.rows[0];
|
const newMessage = result.rows[0];
|
||||||
//console.log(formattedMessage);
|
|
||||||
|
|
||||||
// Emit message to the sender's and recipient's rooms
|
// 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 });
|
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) {
|
} catch (err) {
|
||||||
console.error('Error fetching inserted message:', 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) => {
|
socket.on('get messages', async (recipient) => {
|
||||||
const username = socket.user.username;
|
const username = socket.user.username;
|
||||||
@@ -108,17 +92,16 @@ function initializeSocket(server) {
|
|||||||
//console.log('Sending historical messages');
|
//console.log('Sending historical messages');
|
||||||
socket.emit('messages history', result.rows);
|
socket.emit('messages history', result.rows);
|
||||||
} else {
|
} else {
|
||||||
io.emit('no messages', );
|
io.emit('no messages');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error retrieving messages:', e);
|
console.error('Error retrieving messages:', e);
|
||||||
|
socket.emit('socket error', "Error retrieving messages, refresh the page")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// disconnect event
|
// disconnect event
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
console.log(username + ' has disconnected');
|
return "Disconnected";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const input = document.getElementById('input');
|
|||||||
const recipientForm = document.getElementById('recipientForm');
|
const recipientForm = document.getElementById('recipientForm');
|
||||||
const recipientInput = document.getElementById('recipient');
|
const recipientInput = document.getElementById('recipient');
|
||||||
const messages = document.getElementById('messages');
|
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')}`;
|
document.getElementById('input').placeholder = `Send message as: ${localStorage.getItem('username')}`;
|
||||||
let currentRecipient = null;
|
let currentRecipient = null;
|
||||||
|
|
||||||
@@ -11,23 +11,7 @@ window.onload = () => {
|
|||||||
document.getElementById('recipient').focus();
|
document.getElementById('recipient').focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function initializeRecipient() {
|
function initializeRecipient() {
|
||||||
const savedRecipient = localStorage.getItem('currentRecipient');
|
const savedRecipient = localStorage.getItem('currentRecipient');
|
||||||
if (savedRecipient) {
|
if (savedRecipient) {
|
||||||
@@ -71,7 +55,7 @@ async function initializeSocket() {
|
|||||||
socket.emit('get messages', initialRecipient);
|
socket.emit('get messages', initialRecipient);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('chat message', (msg, serverOffset) => {
|
socket.on('chat message', (msg) => {
|
||||||
console.log('Received message:', msg);
|
console.log('Received message:', msg);
|
||||||
|
|
||||||
const { username, content, recipient } = msg;
|
const { username, content, recipient } = msg;
|
||||||
@@ -88,6 +72,11 @@ async function initializeSocket() {
|
|||||||
console.log('No previous messages found');
|
console.log('No previous messages found');
|
||||||
messages.innerHTML = '<p>No messages found</p>';
|
messages.innerHTML = '<p>No messages found</p>';
|
||||||
});
|
});
|
||||||
|
socket.on('socket error', (msg) => {
|
||||||
|
console.log('There was an error: ', msg)
|
||||||
|
error.innerHTML = msg;
|
||||||
|
messages.innerHTML = '<p>No messages found</p>';
|
||||||
|
})
|
||||||
recipientForm.addEventListener('submit', (e) => {
|
recipientForm.addEventListener('submit', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (recipientInput.value) {
|
if (recipientInput.value) {
|
||||||
|
|||||||
18
frontend/js/settings.js
Normal file
18
frontend/js/settings.js
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,25 +1,32 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
<title>Chat</title>
|
<title>Chat</title>
|
||||||
<link rel="stylesheet" href="static/stylesheet/chat.css">
|
<link rel="stylesheet" href="/static/stylesheet/chat.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<form id="recipientForm">
|
<form id="recipientForm">
|
||||||
<input id="recipient" autocomplete="off" placeholder="Enter recipient"/>
|
<input id="recipient" autocomplete="off" placeholder="Enter recipient"/>
|
||||||
<button type="submit">Set recipient</button>
|
<button type="submit">Set recipient</button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<ul id="messages"></ul>
|
<ul id="messages"></ul>
|
||||||
|
|
||||||
<form id="form" action="">
|
<form id="form" action="">
|
||||||
<input id="input" autocomplete="off" placeholder="Enter message"/>
|
<input id="input" autocomplete="off" placeholder="Enter message"/>
|
||||||
|
<p id="error"></p>
|
||||||
<button type="submit">Send</button>
|
<button type="submit">Send</button>
|
||||||
<button id="logout" type="button">Logout</button>
|
<button type="button" id="settings" onclick="document.location.href='/settings';">Settings</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
<script src="/static/js/chat.js"></script>
|
<script src="/static/js/chat.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
17
frontend/routes/settings.html
Normal file
17
frontend/routes/settings.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Settings</title>
|
||||||
|
<link rel="stylesheet" href="/static/stylesheet/chat.css">
|
||||||
|
<script src="/static/js/settings.js" defer></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>SETTINGS</h1>
|
||||||
|
<button type=button id="logout">Logout</button>
|
||||||
|
<button type="button" id="changePassword">Change password</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -89,14 +89,16 @@ button:hover {
|
|||||||
background-color: #155bb5;
|
background-color: #155bb5;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logout {
|
#settings {
|
||||||
background-color: #d32f2f;
|
background-color: #1a73e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logout:hover {
|
#settings:hover {
|
||||||
background-color: #b71c1c;
|
background-color: #1a73e8;
|
||||||
|
}
|
||||||
|
#error {
|
||||||
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
#messages {
|
#messages {
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
|||||||
123
index.js
123
index.js
@@ -44,17 +44,17 @@ app.post('/auth/signup', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// logout API
|
// logout API
|
||||||
app.post('/auth/logout', (req, res) => {
|
app.post('/auth/logout', (req, res) => {
|
||||||
// clear JWT token
|
// clear JWT token
|
||||||
res.clearCookie('token', {
|
res.clearCookie('token', {
|
||||||
path: '/'
|
path: '/'
|
||||||
});
|
});
|
||||||
// clear socket.io cookie (no idea what is it for)
|
// clear socket.io cookie (no idea what is it for but better remove it)
|
||||||
res.clearCookie('io', {
|
res.clearCookie('io', {
|
||||||
path: '/'
|
path: '/'
|
||||||
});
|
});
|
||||||
res.status(200).json({ message: 'Successfully logged out'});
|
res.status(200).json({ message: 'Successfully logged out'});
|
||||||
})
|
});
|
||||||
|
|
||||||
// get JWT token API
|
// get JWT token API
|
||||||
app.get('/auth/token', (req, res) => {
|
app.get('/auth/token', (req, res) => {
|
||||||
@@ -63,7 +63,25 @@ app.get('/auth/token', (req, res) => {
|
|||||||
res.send('Not logged in');
|
res.send('Not logged in');
|
||||||
}
|
}
|
||||||
res.send(token);
|
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
|
// serving the login page
|
||||||
app.get('/login', (req, res) => {
|
app.get('/login', (req, res) => {
|
||||||
@@ -79,25 +97,27 @@ app.get('/login', (req, res) => {
|
|||||||
app.get('/signup', (req, res) => {
|
app.get('/signup', (req, res) => {
|
||||||
const token = req.cookies.token;
|
const token = req.cookies.token;
|
||||||
if(token){
|
if(token){
|
||||||
res.json({Error: 'Already logged in'});
|
res.json({ Error: 'Already logged in' });
|
||||||
} else
|
} else
|
||||||
res.sendFile(path.join(__dirname, '/frontend/routes/signup.html'));
|
res.sendFile(path.join(__dirname, '/frontend/routes/signup.html'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/settings', (req, res) => {
|
||||||
app.get('/auth/user', (req, res) => {
|
|
||||||
const token = req.cookies.token;
|
const token = req.cookies.token;
|
||||||
if(token) {
|
|
||||||
jwt.verify(token, jwtSecret, (err, user) => {
|
if(!token) {
|
||||||
if(err) {
|
res.redirect('/login');
|
||||||
return res.status(403).send('Unauthorized');
|
return;
|
||||||
} else {
|
|
||||||
const username = user.username;
|
|
||||||
res.json({username});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
// 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
|
// serving the chat page if logged in
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
@@ -124,37 +144,48 @@ server.listen(port, () => {
|
|||||||
|
|
||||||
// signup function
|
// signup function
|
||||||
async function signupUser(req, res) {
|
async function signupUser(req, res) {
|
||||||
let username = req.body.username.trim();
|
let username = req.body.username;
|
||||||
let password = req.body.password.trim();
|
let password = req.body.password;
|
||||||
|
|
||||||
try {
|
if(username && password){
|
||||||
// Check if user exists
|
try {
|
||||||
const exists = await isUserExists(username);
|
// trimming here to avoid app crash when username or password is undefined
|
||||||
if (exists) {
|
username = username.trim();
|
||||||
console.log('User already exists');
|
password = password.trim();
|
||||||
return res.status(500).send('User already exists!');
|
// 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 <a href=/login>Login screen</a>");
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error inserting data:', err);
|
||||||
|
return res.status(500).send('Error inserting data');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Hash password
|
res.send('Please enter Username and 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 <a href=/login>Login screen</a>");
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error inserting data:', err);
|
|
||||||
return res.status(500).send('Error inserting data');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// login function
|
// login function
|
||||||
async function loginUser(req, res) {
|
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) {
|
if (username && password) {
|
||||||
try {
|
try {
|
||||||
|
username = username.trim();
|
||||||
|
password = password.trim();
|
||||||
|
|
||||||
const result = await db.query('SELECT * FROM accounts WHERE username = $1', [username]);
|
const result = await db.query('SELECT * FROM accounts WHERE username = $1', [username]);
|
||||||
// check if user exists
|
// check if user exists
|
||||||
if (result.rows.length > 0) {
|
if (result.rows.length > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user