In an effort to teach myself Node.JS and truly delve into its communication capabilities, I elected to recreate a previous chatting project. It took a bit of forethought to acclimate code to the asynchronous nature of node, consider the implications of sending files for access and get in the habit of using callbacks.
I wonder if and where I may be over-complicating or over-simplifying.
registration.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="keywords" content="registration, register, chat, chatroom"/>
<meta name="robots" content="index, follow"/>
<meta name="author" content="Luigi Vincent"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Web Chatter</title>
<style>
body {
background-color: #E9EAED;
color: #141823;
}
#title {
color: #415E9A;
text-align: center;
}
#register-form {
margin-top: 10em;
text-align: center;
}
.login {
margin: 0.5em;
border-radius: 0.6em;
color: #ffffff;
cursor: pointer;
font-size: 1em;
background-color: #415E9A;
}
.login:hover {
box-shadow: 0 0 10px #4A66A0;
}
.login:active {
position: relative;
outline: none;
top: 0.05em;
}
#login {
margin-left: 6em;
}
.response {
font-weight: bold;
text-align: center;
}
.success {
color: #4A66A0;
}
.error {
color: #FFCCCC;
}
</style>
</head>
<body>
<form id='register-form' method="post">
<h1 id='title'>Welcome to Chatter</h1>
Username: <input type="text" class="input" name="username" id="username" required="required"><br>
Password: <input type="password" class="input" name="password" id="password" style="margin-left: 3px;" required="required"><br>
Re-enter Password: <input type="password" class="input" name="confirm-password" id="confirm-password" style="margin-right: 55px;" required="required"><br>
<input class="login" id="new" type="submit" name="register" value="Register" style="margin-left: 12em;">
</form>
<script src="/assets/sweetalert/dist/sweetalert.min.js"></script>
<link rel="stylesheet" type="text/css" href="/assets/sweetalert/dist/sweetalert.css">
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(e) {
e.preventDefault();
if ($('#password').val() === $('#confirm-password').val()) {
socket.emit('registration', $('#username').val(), $('#password').val());
} else {
swal("Oops...", "Passwords do not match", "error");
}
});
socket.on('redirect', function(page){
window.location.href = page;
});
socket.on('exists', function() {
swal("Sorry", "Username is taken", "error");
});
</script>
</body>
</html>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="A web chatting application by LKX"/>
<meta name="keywords" content="chat, app, application, chatroom"/>
<meta name="robots" content="index, follow"/>
<meta name="author" content="Luigi Vincent, Kerules Fareg, Xiaoding Lin"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Web Chatter</title>
<style>
body {
background-color: #E9EAED;
color: #141823;
}
#title {
color: #415E9A;
text-align: center;
}
#login-form {
margin-top: 10em;
text-align: center;
}
.login {
margin: 0.5em;
border-radius: 0.6em;
color: #ffffff;
cursor: pointer;
font-size: 1em;
background-color: #415E9A;
}
.login:hover {
box-shadow: 0 0 10px #4A66A0;
}
.login:active {
position: relative;
outline: none;
top: 0.05em;
}
#login {
margin-left: 6em;
}
.response {
font-weight: bold;
text-align: center;
}
.success {
color: #4A66A0;
}
.error {
color: #FFCCCC;
}
</style>
</head>
<body>
<form id='login-form' method=''>
<h1 id='title'>Welcome to Chatter</h1>
Username: <input type="text" class="input" id="username" required="required"><br>
Password: <input type="password" class="input" id="password" style="margin-left: 3px;" required="required"><br>
<input class="login" id="new" type="button" name="register" value="New User">
<input class="login" id="login" type="submit" name="submit" value="Log in">
</form>
<script src="/assets/sweetalert/dist/sweetalert.min.js"></script>
<link rel="stylesheet" type="text/css" href="/assets/sweetalert/dist/sweetalert.css">
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(e) {
e.preventDefault();
socket.emit('login', $('#username').val(), $('#password').val());
});
socket.on('redirect', function(page) {
window.location.href = page;
});
socket.on('invalid', function() {
swal("Oops...", "Wrong username or password", "error");
});
$("#new").click(function() {
socket.emit("register", null);
});
</script>
</body>
</html>
app.html
<!DOCTYPE html>
<html>
<head>
<title>WebChatter</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font: 13px Helvetica, Arial;
background-color: #E9EAED;
color: #141823;
}
form {
background: #000;
padding: 3px;
position: fixed;
bottom: 0;
width: 100%;
}
form input {
border: 0;
padding: 10px;
width: 89%;
margin-right: .5%;
}
form button {
width: 5%;
background: #415E9A;
color: #fff;
border: none;
padding: 10px;
}
form button:hover {
box-shadow: 0 0 10px #4A66A0;
}
#messages {
list-style-type: none;
margin: 0;
padding: 0;
}
#messages li {
padding: 5px 10px;
}
#messages li:nth-child(odd) {
background: #fff;
}
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
<button id="return">Logout</button>
</form>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('connect', function() {
socket.emit('user connect', null);
});
$('#return').click(function () {
window.history.back();
});
socket.on('chat message', function(msg) {
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
</html>
index.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var serveStatic = require('serve-static');
var connectingUser = [];
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'password',
database : 'Chatter'
});
connection.connect();
app.use('/assets', serveStatic(__dirname + '/node_modules'));
app.use(require('serve-favicon')(__dirname + '/assets/icon.ico'));
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
})
app.get('/login', function(req, res) {
res.redirect("/");
})
app.get('/register', function(req, res) {
res.sendFile(__dirname + '/registration.html');
})
app.get('/app', function(req, res) {
res.sendFile(__dirname + '/app.html');
})
io.on('connection', function(socket) {
var user = '';
socket.on('user connect', function() {
retrieveUserList(function(err, userList) {
socket.emit('chat message', userList);
});
user = connectingUser.pop();
});
socket.on('chat message', function(msg) {
if (msg.length > 0) {
io.emit('chat message', user + ": " + msg);
}
});
socket.on('login', function(username, password) {
isValidUser(username, password, function(err, result) {
if (result) {
connectingUser.push(username);
io.emit('chat message', username + " has logged in.");
socket.emit('redirect', '/app');
} else {
socket.emit('invalid', null);
}
});
});
socket.on('register', function() {
socket.emit("redirect", '/register');
});
socket.on('registration', function(username, password) {
isExistingUser(username, function(err, result) {
if (result) {
socket.emit('exists', null);
} else {
addUser(username, password);
socket.emit("redirect", '/login');
}
});
});
});
http.listen(8080, function(){
console.log('listening on: 8080');
});
function addUser(username, password) {
connection.query('INSERT INTO users(username, password) VALUES("'
+ username + '", "' + password +'")',
function(err) {
if (err) {
console.error("Error while performing user addition query.");
}
}
);
}
function isExistingUser(username, callback) {
connection.query("SELECT username FROM users WHERE username=\"" + username + "\"",
function(err, rows) {
if (!err) {
callback(null, rows.length > 0);
} else {
console.error('Error while performing user existential query.');
}
}
);
}
function isValidUser(username, password, callback) {
connection.query("SELECT username FROM users WHERE username=\"" + username
+ "\" AND password=\"" + password + "\"",
function(err, rows) {
if (!err) {
callback(null, rows.length > 0);
} else {
console.error('Error while performing user validation query.');
}
}
);
}
function retrieveUserList(callback) {
connection.query("SELECT username FROM users", function(err, rows) {
var list = 'Registered Users: ';
if (!err) {
var listBuilder = '';
for (var i = 0; i < rows.length; i++) {
listBuilder += ', ' + rows[i].username;
}
list += listBuilder.substring(2);
callback(null, list);
} else {
console.error('Error while performing user list query.');
}
});
}
package.json
{
"name": "socket-chatting-app",
"version": "0.0.6",
"description": "web chatter",
"dependencies": {
"express": "^4.10.2",
"socket.io": "^1.4.5",
"mysql": "^2.5.4"
}
}
I'm especially interested in:
- Any remarks on conventional refactoring.
- Feedback on the way I 'send' files for access.
- My use of callbacks (fairly new concept for me).
- Any feedback on what could be improved in general.
Feel free to try it here.