jsbin/lib/app.js
2013-07-04 11:14:28 +01:00

262 lines
7.8 KiB
JavaScript

var nodemailer = require('nodemailer'),
express = require('express'),
flatten = require('flatten.js').flatten,
path = require('path'),
app = express(),
hogan = require('./hogan'),
options = require('./config'),
spike = require('./spike'),
store = require('./store')(options.store),
routes = require('./routes'),
handlers = require('./handlers'),
middleware = require('./middleware'),
metrics = require('./metrics'),
url = require('url'),
flattened;
var passport = require('passport'),
GitHubStrategy = require('passport-github').Strategy;
/**
* Passport configuration
*/
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
passport.use(new GitHubStrategy({
clientID: options.github.id,
clientSecret: options.github.secret
},
function(accessToken, refreshToken, profile, done) {
// Delete information we don't need that will fill up the cookie jar
//
// .---------------------------.
// /_ _ _ __ __ /|
// // \ / \ / \ |_/ | |_ (_ / |
// / \_ \_/ \_/ | \ | |__ ,_/ / |
// :.__________________________/ /
// | .--. .--. .--. .--. | /
// | ( ) ) ( ) ( ) | /
// | '--' '--' '--' '--' |/
// '---------------------------'
//
profile.email = profile._json.email;
delete profile._raw;
delete profile._json;
delete profile.photos;
return done(null, {
access_token: accessToken,
profile: profile
});
}
));
/**
* JS Bin configuration
*/
app.store = store;
app.mailer = (function (mail) {
var mailTransport = null,
method = mail && mail.adapter,
settings = mail && mail[method];
if (method && options) {
mailTransport = nodemailer.createTransport(method, settings);
}
return new handlers.MailHandler(mailTransport, app.render.bind(app));
})(options.mail);
app.PRODUCTION = 'production';
app.DEVELOPMENT = 'development';
// Set the NODE_ENV variable as this is used by Express, we want the
// environment to take precedence but allow it to be set using the config file
// too.
if (process.env.NODE_ENV) {
options.env = process.env.NODE_ENV;
}
process.env.NODE_ENV = options.env;
// Need to set the node environment to run in the same timezone as the database
// This will ideally be UTC in both cases but if not this can be set either
// using the TZ environment variable or the "timezone" option.
// This is because the mysql library coerces MySQL dates (without timezones)
// into JavaScript date objects.
if (!process.env.TZ && options.timezone) {
process.env.TZ = options.timezone;
}
// Sort out the port.
(function () {
var port = process.env.PORT;
// if we're running from the bin/jsbin file, then we modify
// both the listen port AND the options.url
if (process.env.JSBIN_PORT) {
if (options.url.host.indexOf(':') === -1) {
options.url.host += ':' + process.env.JSBIN_PORT;
} else {
options.url.host = options.url.host.replace(/\:\d+$/, function () {
return ':' + process.env.JSBIN_PORT;
});
}
}
if (!port) {
options.url.host.replace(/\:(\d+)$/, function (m, p) {
if (p.length) {
port = p;
}
});
}
if (!port) {
port = 80;
}
// NOTE: this is *only* used for running the server.listen()
// it's not used in the urls to access assets, etc.
options.port = port;
})();
// Strip trailing slash from the prefix.
options.url.prefix = options.url.prefix.replace(/\/$/, '');
// Apply the keys from the config file. All nested properties are
// space delimited to match the express style.
//
// For example, app.set('url prefix'); //=> '/'
flattened = flatten(options, ' ');
Object.getOwnPropertyNames(flattened).forEach(function (key) {
app.set(key, flattened[key]);
});
// in live, we run behind a proxy - so this will give us our IPs again: http://expressjs.com/guide.html#proxies
if (process.env.NODE_ENV === app.PRODUCTION || process.env.JSBIN_PROXY) {
app.enable('trust proxy');
}
app.set('root', path.resolve(path.join(__dirname, '..')));
app.set('version', require('../package').version);
if (String(process.env.ALLOW_CLIENT_USER).toLowerCase() === 'true') {
app.set('client', { user: true });
} else if (String(process.env.ALLOW_CLIENT_USER).toLowerCase() === 'false') {
app.set('client', { user: false });
} else {
app.set('client', options.client);
}
app.set('view engine', 'html');
app.set('views', path.join(app.set('root'), 'views'));
app.set('url prefix', options.url.prefix);
app.set('url ssl', options.url.ssl);
app.set('url full', (app.set('url ssl') ? 'https://' : 'http://') + app.set('url host') + app.set('url prefix'));
app.set('basepath', app.set('url prefix'));
app.set('is_production', app.get('env') === app.PRODUCTION);
if (options.url.static) {
app.set('static url', (app.set('url ssl') ? 'https://' : 'http://') + app.set('url static'));
} else {
app.set('static url', app.set('url full'));
}
app.engine('html', hogan.renderer).engine('txt', hogan.renderer);
// Define some generic template variables.
app.locals({
version: app.set('version'),
client: app.get('client')
});
app.connect = function (callback) {
app.emit('before', app);
// Register all the middleware.
app.configure(function () {
var mount = app.set('url prefix') || '/',
logger;
logger = process.env.JSBIN_LOGGER || app.set('server logger') || 'tiny';
if (logger !== 'none') {
app.use(express.logger(logger));
}
// Redirect gist.jsbin urls to /gist/:id
app.use(function (req, res, next) {
if (req.headers.host.match(/gist.jsbin/)) {
var parsedUrl = url.parse(req.originalUrl),
segments = parsedUrl.pathname.split('/'),
gistId = segments.slice(-1).join('');
return res.redirect((app.get('url ssl') ? 'https://' : 'http://') + options.url.host + '/gist/' + gistId);
}
next();
});
app.use(mount, express.static(path.join(app.set('root'), 'public')));
app.use(middleware.limitContentLength({limit: app.set('max-request-size')}));
app.use(express.cookieParser(app.set('session secret')));
app.use(express.cookieSession({key: 'jsbin', cookie: {maxAge: 365 * 24 * 60 * 60 * 1000}}));
// If we're in SSL mode but an insecure connection comes in, redirect to
// the SSL version (removing any port information)
app.use(function (req, res, next) {
if (app.get('url ssl') && !req.secure) {
return res.redirect('https://' + req.headers.host.replace(/:.*/, '') + req.url);
}
next();
});
// Passport
app.use(passport.initialize());
app.use(express.urlencoded());
app.use(express.json());
app.use(middleware.csrf({ ignore: ['/', /^\/api\//] }));
app.use(middleware.api({ app: app }));
app.use(middleware.subdomain(app));
app.use(middleware.noslashes());
app.use(middleware.ajax());
app.use(middleware.cors());
app.use(middleware.jsonp());
app.use(middleware.flash());
app.use(mount, app.router);
// Register all routes
routes(app);
});
app.emit('after', app);
store.connect(function (err) {
if (err) {
metrics.increment('error.store.connect');
throw err;
}
var port = app.set('port');
module.exports.listen(port);
if (typeof callback === 'function') {
callback();
}
app.emit('connected');
process.stdout.write('JS Bin v' + app.set('version') + ' is up and running on port ' + app.set('port') + '. Now point your browser at ' + app.set('url full') + '\n');
});
};
// Export the application to allow it to be included.
module.exports = app;
// Run a local development server if this file is called directly.
if (require.main === module) {
app.connect();
}