-
-
Save evaldasg/52e73b2f237dd38466d020bb79436650 to your computer and use it in GitHub Desktop.
Code for my loading strategy, documented at http://tech.tdp.me/2012/08/28/better-spa/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var YUI = require('yui').YUI, | |
Y = YUI(/* Your YUI Config */), | |
requireAuth = require('./lib/middlware/auth'), | |
modelLoader = require('./lib/middlware/loader'), | |
app = /* create express app */; | |
Y.namespace('TDP').config = { | |
webservices : global.config.webservices.path | |
}; | |
Y.use('tdp-model-person'); | |
app.get('/*', | |
requireAuth(Y), | |
modelLoader(Y, [ 'Your', 'Initial', 'Models', 'Here' ]), | |
function(req, res) { | |
res.render( | |
'index', | |
{ | |
access_token : req.session.oauth.access_token, | |
initial_state : Y.JSON.stringify( req.models || { } ) | |
} | |
); | |
} | |
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var config = require('./config/config'), | |
http = require('http'), | |
connect = require('connect'), | |
express = require('express'), | |
YUI = require('yui').YUI, | |
Y = YUI(config.yui.server), | |
modelLoader = require('./client/lib/middleware/loader'), | |
requireAuth = require('./client/lib/middleware/auth'), | |
app = express(); | |
Y.use('parallel', 'handlebars', 'handlebars-helpers', 'model', 'model-sync-rest', 'tdp-model-person', 'tdp-model-goal-list', 'json'); | |
app.get('/login', function(req, res) { | |
res.render('login', { | |
form : { | |
action : '/login', | |
fields : [ | |
{ label : 'Email Address', name : 'email', type : 'email', autofocus : true }, | |
{ label : 'Password', type : 'password', name : 'password' } | |
] | |
} | |
}); | |
}); | |
app.post('/login', | |
requireAuth(Y), | |
function(req, res) { | |
/** | |
If we got here, we are authenticated successfully, look in the | |
middleware for implementation details. | |
**/ | |
res.redirect('/'); | |
} | |
); | |
app.get('/logout', | |
function(req, res) { | |
Y.log('Destroying session'); | |
req.session.destroy(); | |
res.redirect('/login'); | |
} | |
); | |
/** | |
Wild card fetch should look at the request part, and render the view specifically required. | |
This is very application specific, so I'm just showing rendering the index page. | |
**/ | |
app.get('/*', | |
requireAuth(Y), | |
// Make sure that any models you list here, you've included in the Y.use line above! | |
modelLoader(Y, [ 'YourModels' ]), | |
function(req, res) { | |
res.render( | |
'index', | |
{ | |
// Pass the access_token and initial data, serializing the models | |
// into JSON for quick easy loading. Just this step alone cuts down | |
// initial load times a lot! | |
access_token : req.session.oauth.access_token, | |
initial_state : Y.JSON.stringify( req.models || { } ) | |
} | |
); | |
} | |
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports = module.exports = function checkAuthentication(Y) { | |
var NS = Y.namespace('TDP'), | |
/** | |
This is our model for handling all authentication. Tt will query against | |
two different URLs depending upon if it needs a code or an access token. | |
**/ | |
OAuthModel = Y.Base.create('oauthModel', Y.Model, [ Y.ModelSync.REST ], | |
{ | |
get_token : global.config.webservices.path + '/api/oauth/access_token', | |
authorize : global.config.webservices.path + '/api/oauth/authorize', | |
getURL : function() { | |
if ( this.get('code') ) { | |
this.url = this.get_token; | |
} else { | |
this.url = this.authorize; | |
} | |
return Y.ModelSync.REST.prototype.getURL.apply(this, arguments); | |
} | |
}, | |
{ | |
ATTRS : { | |
email : { }, | |
password : { }, | |
// These should *only* be available on the server. They are private! | |
client_id : { value : 'your-app-client-id' }, | |
client_secret : { value : 'your-app-client-secret' }, | |
access_token : { }, | |
token_type : { }, | |
code : { } | |
} | |
} | |
); | |
/** | |
The Express handler for determining if the user is logged in. | |
**/ | |
return function(req, res, next) { | |
// We attach models to the request, so we can load things as we go and | |
// keep them | |
if ( ! Y.Lang.isObject( req.models ) ) { | |
req.models = { }; | |
} | |
var oauth, | |
person = new NS.Person(); | |
// Do we have an auth token to try? If so, use that and try to load the | |
// person. If it fails, we go to the login page. | |
if ( req.session.oauth && req.session.oauth.access_token ) { | |
oauth = new OAuthModel(req.session.oauth); | |
person.load( | |
{ headers : { 'X-Access-Token' : oauth.get('access_token') } }, | |
function(err, res) { | |
if ( err && err.code === 403 ) { | |
res.redirect('/login'); | |
return; | |
} | |
req.models.Person = person; | |
next(); | |
} | |
); | |
} | |
else if ( req.method === 'POST' && req.body.email && req.body.password ) { | |
oauth = new OAuthModel({ | |
email : req.body.email, | |
password : req.body.password | |
}); | |
// The first save is the password test. | |
oauth.save( function(err, res) { | |
if ( err ) { | |
Y.log('Failed authentication, going to login page', 'debug'); | |
req.session.error = res; | |
res.redirect('/login'); | |
return; | |
} | |
oauth.save( function(err, res) { | |
if ( err ) { | |
Y.log('Error fetching user token.', 'debug'); | |
req.session.error = res; | |
res.redirect('/login'); | |
return; | |
} | |
req.session.oauth = oauth.toJSON(); | |
person.load( | |
{ headers : { 'X-Access-Token' : oauth.get('access_token') } }, | |
function(err, res) { | |
if ( err ) { | |
req.session.error = err; | |
res.redirect('/login'); | |
return; | |
} | |
next(); | |
} | |
); | |
}); | |
}); | |
} else { | |
res.redirect('/login'); | |
return; | |
} | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var PersonModel = Y.Base.create('myModel', Y.Model, [ Y.ModelSync.REST ], { }), | |
person = new PersonModel(); | |
person.load( | |
{ headers : { 'X-Access-Token' : access_token } }, | |
function(err, res) { | |
// Server returned an error, need a new access token. | |
if ( err ) { // Additionally, you can check err.code === 403 | |
res.redirect('/login'); | |
return; | |
} | |
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports = module.exports = function loadModels(Y, models) { | |
var NS = Y.namespace('TDP'); | |
return function(req, res, next) { | |
var stack = new Y.Parallel(), | |
oauth = req.session.oauth; | |
if ( ! Y.Lang.isObject( req.models ) ) { | |
req.models = { }; | |
} | |
if ( !oauth ) { | |
Y.log('No oauth in the session. You must define this first.', 'error'); | |
res.redirect('/login'); | |
return; | |
} | |
Y.Array.each( models, function(name) { | |
// Assume this model is already loaded. | |
if ( req.models[name] ) { | |
return; | |
} | |
var model = new NS[name](); | |
model.load( | |
{ headers : { 'X-Access-Token' : oauth.access_token } }, | |
stack.add( function(err) { | |
if ( err ) { | |
Y.log('Failed loading model ' + name + ' with error:'); | |
Y.log(err); | |
} else { | |
Y.log('Setup a model successfully'); | |
req.models[name] = model; | |
} | |
}) | |
); | |
}); | |
stack.done( function() { next(); } ); | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment