Skip to content

Commit

Permalink
🗿 Added a real-time Dashboard with Socket.io
Browse files Browse the repository at this point in the history
  • Loading branch information
dstroot committed Jan 29, 2014
1 parent ec4f29b commit 0a632de
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -7,6 +7,7 @@ lib-cov
*.pid
*.gz
*.swp
*.css

pids
logs
Expand Down
78 changes: 72 additions & 6 deletions app.js
Expand Up @@ -10,7 +10,11 @@ var path = require('path');
var mongoose = require('mongoose');
var passport = require('passport');
var expressValidator = require('express-validator');

var http = require('http');
var io = require('socket.io');
var app = express()
, server = require('http').createServer(app)
, io = io.listen(server);

/**
* Load controllers.
Expand All @@ -20,6 +24,7 @@ var homeController = require('./controllers/home');
var userController = require('./controllers/user');
var apiController = require('./controllers/api');
var contactController = require('./controllers/contact');
var dashboardController = require('./controllers/dashboard');

/**
* API keys + Passport configuration.
Expand All @@ -37,11 +42,15 @@ mongoose.connection.on('error', function() {
console.log('✗ MongoDB Connection Error. Please make sure MongoDB is running.'.red);
});

var app = express();

/**
* Express configuration.
*/

var hour = 3600000; //milliseconds
var day = (hour * 24);
var week = (day * 7);
var month = (day * 30);

app.locals.cacheBuster = Date.now();
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
Expand Down Expand Up @@ -69,17 +78,26 @@ app.use(function(req, res, next) {
app.use(flash());
app.use(less({ src: __dirname + '/public', compress: true }));
app.use(app.router);
app.use(express.static( path.join(__dirname, 'public'), { maxAge: 864000000 } ));
app.use(express.static( path.join(__dirname, 'public'), { maxAge: week } ));
app.use(function(req, res) {
res.render('404', { status: 404 });
});
app.use(express.errorHandler());

/**
* Start Server
*/

server.listen(app.get('port'), function(){
console.log("✔ Express server listening on port %d in %s mode", app.get('port'), app.settings.env);
});

/**
* Application routes.
*/

app.get('/', homeController.index);
app.get('/dashboard', dashboardController.getDashboard);
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/logout', userController.logout);
Expand Down Expand Up @@ -118,6 +136,54 @@ app.get('/auth/foursquare/callback', passport.authorize('foursquare', { failureR
app.get('/auth/tumblr', passport.authorize('tumblr'));
app.get('/auth/tumblr/callback', passport.authorize('tumblr', { failureRedirect: '/api' }), function(req, res) { res.redirect('/api/tumblr'); });

app.listen(app.get('port'), function() {
console.log('✔ Express server listening on port ' + app.get('port'));
/**
* Emit Pageviews on Socket.io
*/

io.configure('production', function(){
io.enable('browser client minification'); // send minified client
io.enable('browser client etag'); // apply etag caching logic based on version number
io.enable('browser client gzip'); // gzip the file
io.set('log level', 1); // reduce logging
io.set("polling duration", 10); // increase polling frequency
io.set('transports', [ // Manage transports
'websocket'
, 'htmlfile'
, 'xhr-polling'
, 'jsonp-polling'
]);
io.set('authorization', function (handshakeData, callback) {
if (handshakeData.xdomain) {
callback('Cross-domain connections are not allowed');
} else {
callback(null, true);
}
});
});

io.configure('development', function(){
io.set('log level', 1); // reduce logging
io.set('transports', [
'websocket' // Let's just use websockets for development
]);
io.set('authorization', function (handshakeData, callback) {
if (handshakeData.xdomain) {
callback('Cross-domain connections are not allowed');
} else {
callback(null, true);
}
});
});

io.sockets.on('connection', function (socket) {
socket.on('message', function (message) {
console.log("Got message: " + message);
var ip = socket.handshake.address.address;
var url = message;
io.sockets.emit('pageview', { 'connections': Object.keys(io.connected).length, 'ip': ip, 'url': url, 'xdomain': socket.handshake.xdomain, 'timestamp': new Date()});
});
socket.on('disconnect', function () {
console.log("Socket disconnected");
io.sockets.emit('pageview', { 'connections': Object.keys(io.connected).length});
});
});
10 changes: 10 additions & 0 deletions controllers/dashboard.js
@@ -0,0 +1,10 @@
/**
* GET /
* Home page.
*/

exports.getDashboard = function(req, res) {
res.render('dashboard', {
title: 'Dashboard'
});
};
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -28,6 +28,7 @@
"twit": "~1.1.12",
"underscore": "~1.5.2",
"paypal-rest-sdk": "~0.6.4",
"connect-mongo": "~0.4.0"
"connect-mongo": "~0.4.0",
"socket.io": "0.9.16"
}
}
21 changes: 21 additions & 0 deletions public/css/styles.less
Expand Up @@ -30,6 +30,27 @@ body {
border-top: 1px solid @navbar-default-border;
}

// Dashboard
// -------------------------

#connections {
text-align: center;
p {
font-size: 96px;
line-height: 96px;
color: #ff6600;
text-shadow: 1px 1px 1px #222;
}
}

#visits thead tr td {
font-weight: bold;
}

#pageViews thead tr td {
font-weight: bold;
}

// Navbar
// -------------------------

Expand Down
49 changes: 49 additions & 0 deletions views/dashboard.jade
@@ -0,0 +1,49 @@
extends layout

block content

.row
.well.col-md-2#connections
h3 Right Now
p 0
h5 active visitors
.col-md-10
legend Real Time Activity
table#visits.table.table-bordered.table-striped.table-condensed
thead
tr
td URL
td IP
td Timestamp
tbody
legend Page Views
table#pageViews.table.table-bordered.table-striped.table-condensed
thead
tr
td URL
td Page Views
tbody

script.
var pages = {};
var lastPageId = 0;
socket.on('connect', function () {
console.log('Socket connected');
socket.on('pageview', function (msg) {
console.log('Connections: ' + msg.connections);
$('#connections > p').html(msg.connections - 1); // -1 since we don't count our own dashboard connection
if (msg.url) {
if ($('#visits tr').length > 10) {
$('#visits tr:last').remove();
}
$('#visits tbody').prepend('<tr><td>' + msg.url + '</td><td>' + msg.ip + '</td><td>' + msg.timestamp + '</td></tr>');
if (pages[msg.url]) {
pages[msg.url].views = pages[msg.url].views + 1;
$('#page' + pages[msg.url].pageId).html(pages[msg.url].views);
} else {
pages[msg.url] = {views: 1, pageId: ++lastPageId};
$('#pageViews tbody').append('<tr><td>' + msg.url + '</td><td id="page' + lastPageId + '">1</td></tr>');
}
}
});
});
10 changes: 10 additions & 0 deletions views/layout.jade
Expand Up @@ -17,6 +17,16 @@ html
script(src='/js/lib/jquery.js?v=#{cacheBuster}')
script(src='/js/lib/bootstrap.js?v=#{cacheBuster}')
script(src='/js/main.js?v=#{cacheBuster}')
script(src='/socket.io/socket.io.js?v=#{cacheBuster}')
//- For real-time monitoring
script.
var socket = io.connect();
socket.on('connect', function () {
socket.send(window.location.href);
});
window.onhashchange = function () {
socket.send(window.location.href);
}
body
#wrap
include partials/navigation
Expand Down
2 changes: 2 additions & 0 deletions views/partials/navigation.jade
Expand Up @@ -15,6 +15,8 @@
a(href='/api') API Browser
li(class=title=='Contact'?'active':undefined)
a(href='/contact') Contact
li(class=title=='Dashboard'?'active':undefined)
a(href='/dashboard') Dashboard
ul.nav.navbar-nav.navbar-right
if !user
li(class=title=='Login'?'active':undefined)
Expand Down

1 comment on commit 0a632de

@usamamashkoor
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can i use socket.io in the controller...?

Please sign in to comment.