Skip to content

Instantly share code, notes, and snippets.

@olanb7
Forked from jshirley/app-short.js
Created November 9, 2012 14:46
Show Gist options
  • Save olanb7/4046092 to your computer and use it in GitHub Desktop.
Save olanb7/4046092 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/
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 || { } )
}
);
}
);
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 || { } )
}
);
}
);
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;
}
}
};
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;
}
);
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