index.js | |
---|---|
Frontoffice sub-app for quiz running | var fs = require('fs');
var passport = require('passport');
var path = require('path');
var TwitterStrategy = require('passport-twitter').Strategy;
var Sequelize = require('sequelize');
var Quiz = require('../models/quiz');
var engine = require('../engine');
var _ = require('underscore');
var io = require('socket.io');
var localIP = require('../client/local_ip').localIP;
module.exports = frontOfficeApp;
var OAUTH_CALLBACK_PATH = '/ohai'; |
Subapp setup | |
This is the function this module exports. Because Express will block any further middleware once a route is registered, this behaves in two modes: middleware (non-route) and route (non-middleware). Calling it without a mode (or with an invalid mode) does everything. | function frontOfficeApp(app, mode, server) { |
Middleware-only or generic context | if ('routes' !== mode) {
bindWebSockets(server); |
Subapp-local view path | app.use('/front', function useLocalViews(req, res, next) {
app.set('views', path.join(__dirname, 'views'));
next();
});
} |
Routes-only or generic context | if ('middleware' !== mode) { |
Root access should redirect to the frontoffice subapp | app.all('/', function(req, res) {
res.redirect(301, '/front');
}); |
Namespaced quiz running routes | app.namespace('/front', function() {
app.get('/', mainPage); |
Subapp authentication (using a previously registered Twitter strategy, see the bottom of this file) | app.get('/auth', passport.authenticate('twitter')); |
OAuth callback for the Twitter strategy | app.get(OAUTH_CALLBACK_PATH, passport.authenticate('twitter', {
successRedirect: '/front',
failureFlash: true,
failureRedirect: '/front'
}));
});
}
} |
Action: main page (initial rendering) | function mainPage(req, res) {
engine.checkAuth(req, res, function() {
if (engine.currentQuiz) {
engine.getUsers(function(err, users) {
if (err) throw err;
res.render('index', { user: req.user, engine: engine, users: users });
});
} else {
res.render('index', { user: req.user, engine: engine });
}
});
} |
WebSockets manager | |
This binds a WebSockets layer over the HTTP app and provides the gateway between WS traffic and the engine (both ways). | function bindWebSockets(server) {
var sio = io.listen(server);
sio.set('log level', 2); |
Convenience forwarder when the WS "event" is exactly the same as the engine-emitted event. | function justForward(call) {
engine.on(call, function() {
sio.sockets.emit.apply(sio.sockets, _.flatten([call, arguments]));
});
} |
Quiz init: notify waiting clients ("No active quiz yet…" front screens) | engine.on('quiz-init', function(quiz) {
engine.getUsers(function(err, users) {
if (err) throw err;
sio.sockets.emit('quiz-init', _.pick(quiz, 'title', 'description', 'level'), users);
});
}); |
Quiz join: a new user comes in the engine while a quiz is at init stage. | justForward('quiz-join'); |
Question start: a new question starts! (including quiz start) | justForward('question-start'); |
Answers coming in (input) | sio.sockets.on('connection', function(socket) {
socket.on('answer', function(answer) {
socket.set('userId', answer.userId);
engine.handleAnswer(answer);
});
}); |
Answers getting in (output) | justForward('new-answer');
justForward('edit-answer'); |
Question ends! | justForward('question-end'); |
Quiz ends! | engine.on('quiz-end', function(scoreboard) { |
For every socket, check whether it's a player, and if so get and send their scoring. | sio.sockets.clients().forEach(function(socket) {
socket.get('userId', function(err, userId) {
var scoring = userId && _.findWhere(scoreboard, { id: userId });
if (scoring) {
scoring.rank = scoreboard.indexOf(scoring) + 1;
}
socket.emit('quiz-end', scoring);
});
});
});
} |
Frontoffice authentication setup | |
Read credentials off a JSON file in this file's directory and initialize a Passport Twitter OAuth strategy with those. | function readCredentials(cb) {
fs.readFile(path.join(__dirname, 'credentials.json'), function(err, json) {
if (err)
console.warn("Missing frontoffice credentials -> You won't be able to authenticate!");
var creds = JSON.parse(json || '{}');
if (creds.consumerKey && creds.consumerSecret)
console.log("Front credentials loaded");
else
console.log("One or more blank front credential -> You won't be able to authenticate!");
cb(creds);
});
} |
Read the OAuth credentials (consumer key, consumer secret) for the Twitter App you registered as a developer, then define a Twitter strategy the Passport authentication middleware can use (see above) | readCredentials(function(creds) {
console.log('OAuth callback IP', localIP);
passport.use(new TwitterStrategy(
_.extend(creds, { callbackURL: 'http://' + localIP + ':3000/front' + OAUTH_CALLBACK_PATH }),
function(token, tokenSecret, profile, done) {
var user = {
id: profile.id,
name: '@' + profile.displayName,
avatar: (profile.photos[0] || {}).value
};
console.log('TWITTER USER: ', user);
done(null, user);
}
));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
});
|