jsbin/lib/app.js
2012-06-10 14:43:21 +01:00

185 lines
5.3 KiB
JavaScript

var express = require('express'),
flatten = require('flatten').flatten,
hogan = require('hogan.js'),
gobo = require('gobo'),
path = require('path'),
fs = require('fs'),
app = express.createServer(),
errors = require('./errors'),
options = require('./config'),
spike = require('./spike'),
store = require('./store')(options.store),
models = require('./models'),
handlers = require('./handlers'),
middleware = require('./middleware'),
binHandler = new handlers.BinHandler(),
sessionHandler = new handlers.SessionHandler(),
flattened;
app.store = store;
app.spike = spike;
app.models = models.createModels(store);
app.templates = {};
app.PRODUCTION = 'production';
app.DEVELOPMENT = 'development';
// 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]);
});
app.set('root', path.resolve(path.join(__dirname, '..')));
app.set('version', require('../package').version);
app.set('url prefix', options.url.prefix.replace(/\/$/, ''));
app.set('url full', (app.set('url ssl') ? 'https://' : 'http://') + app.set('url host') + app.set('url prefix'));
// Register all the middleware.
app.use(express.logger());
app.use(express.static(path.join(app.set('root'), 'public')));
app.use(express.cookieParser(app.set('session secret')));
app.use(express.cookieSession({key: 'jsbin'}));
app.use(express.bodyParser());
app.use(express.csrf());
app.use(middleware.subdomain(app));
app.use(middleware.noslashes());
app.use(middleware.ajax());
app.use(middleware.cors());
app.use(middleware.jsonp());
app.use(middleware.helpers(app));
// Create a Hogan/Mustache handler for templates.
app.engine('html', function (path, options, fn) {
fs.readFile(path, 'utf8', function (err, template) {
if (err) {
return fn(err);
}
try {
var compiled = app.templates[path];
if (!compiled) {
compiled = app.templates[path] = hogan.compile(template);
}
fn(null, compiled.render(options));
} catch (error) {
fn(error);
}
});
});
// Events
binHandler.on('updated', spike.ping.bind(spike));
binHandler.on('render-scripts', spike.appendScripts.bind(spike));
// Configure the template engine.
app.set('view engine', 'html');
app.set('views', path.join(app.set('root'), 'views'));
// Define some generic template variables.
app.locals({
version: app.set('version')
});
// Load the bin from the store when encountered in the url. Also handles the
// "latest" url action.
app.param('bin', binHandler.loadBin);
// Set up the routes.
app.get('/', binHandler.getDefault);
// Login/Create account.
app.post('/sethome', sessionHandler.loadUser, sessionHandler.loginUser, sessionHandler.createUser);
app.post('/logout', sessionHandler.logoutUser);
// List
app.get('/list', binHandler.getUserBins);
// Latest
app.get('/:bin/latest((.|\/):format)?', binHandler.redirectToLatest);
app.get('/:bin/latest/edit', binHandler.redirectToLatest);
// Edit
app.get('/:bin/:rev?/edit', binHandler.getBin);
// Save
app.post('/save', binHandler.createBin);
app.post('/:bin/:rev?/save', binHandler.createRevision);
// Download
app.get('/:bin/:rev?/download', binHandler.downloadBin);
// Source
app.get('/:bin/:rev?/source', binHandler.getBinSource);
app.get('/:bin/:rev?.:format(js|json|css|html)', binHandler.getBinSourceFile);
app.get('/:bin/:rev?/:format(js)', function (req, res) {
// Redirect legacy /js suffix to the new .js extension.
res.redirect(301, req.path.replace(/\/js$/, '.js'));
});
// Log
app.get('/:bin/:rev/log', spike.getLog);
app.post('/:bin/:rev/log', spike.postLog);
// Preview
app.get('/:bin/:quiet(quiet)?', binHandler.getBinPreview);
app.get('/:bin/:rev?/:quiet(quiet)?', spike.getStream, binHandler.getBinPreview);
// Serve the concatenated JavaScript.
app.get('/js/debug/jsbin.js', function (req, res) {
var js = path.join(app.set('root'), 'public', 'js');
gobo(path.join(js, 'jsbin.js'), 'vendor', function (content) {
res.contentType('js');
res.send(content);
});
});
// Error handler.
app.use(function (err, req, res, next) {
if (err instanceof errors.NotFound) {
return binHandler.notFound(req, res);
}
next(err);
});
// Final connect in error handler. Ideally should never hit this.
app.use(express.errorHandler({showStack: true, dumpExceptions: true}));
// Export the application to allow it to be included.
module.exports = app;
if (app.set('url prefix') !== '/') {
// If we have a prefix then mount the app within another
// express app to save us hacking around with the routes.
module.exports = express().use(app.set('url prefix'), app);
}
// Run a local development server if this file is called directly.
if (require.main === module) {
store.connect(function (err) {
if (err) {
throw err;
}
var port = process.env.PORT;
if (!port) {
options.url.host.replace(/\:(\d+)$/, function (m, p) {
if (p.length) {
port = p;
}
});
}
if (!port) {
port = 80;
}
process.stdout.write('Running jsbin on port ' + port + '\n');
module.exports.listen(port);
});
}