From 74e100f0654629ee9f1b5abbdac9e72e8e6361cd Mon Sep 17 00:00:00 2001 From: Daniel Wilson Date: Thu, 10 Apr 2014 09:26:35 -0500 Subject: [PATCH 01/14] move lessjs from core to starter --- bower.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 93ee774..3ff7471 100644 --- a/bower.json +++ b/bower.json @@ -3,7 +3,7 @@ "version": "2.3.3", "homepage": "http://getlavaca.com", "authors": [ - "Mutual Mobile " + "Mutual Mobile " ], "license": "MIT", "ignore": [ @@ -14,6 +14,7 @@ "tests" ], "dependencies": { - "lavaca": "latest" + "lavaca": "latest", + "less.js": "1.7.x" } } From 63c4c3852bf93957c7301a2290fec8e2b975b074 Mon Sep 17 00:00:00 2001 From: Daniel Wilson Date: Thu, 10 Apr 2014 09:50:32 -0500 Subject: [PATCH 02/14] index.html to use less 1.7.0 in local env --- src/www/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/www/index.html b/src/www/index.html index 2826da2..6af4c0c 100755 --- a/src/www/index.html +++ b/src/www/index.html @@ -61,7 +61,7 @@ var less=less||{}; less.env='development'; - + From fb1e93bed6b5112b298117161e9576e4436d5512 Mon Sep 17 00:00:00 2001 From: Robert Williams Date: Tue, 15 Apr 2014 12:04:22 -0500 Subject: [PATCH 03/14] added /seo path that should provide indexable content. added phantomjs and portscanner. --- package.json | 5 ++++- src/www/index.html | 28 ++++++++++++++-------------- src/www/js/app/boot.js | 2 +- src/www/robots.txt | 1 + tasks/server/server.js | 26 +++++++++++++++++++++++++- 5 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 src/www/robots.txt diff --git a/package.json b/package.json index 0d90679..9833f19 100755 --- a/package.json +++ b/package.json @@ -30,7 +30,10 @@ "dustjs-linkedin": "2.0.2", "dustjs-helpers": "1.1.1", "grunt-shell": "0.3.x", - "bower": "1.2.x" + "bower": "1.2.x", + "phantomjs": "1.9.x", + "phantom": "0.5.7", + "portscanner": "0.2.3" }, "scripts": { "postinstall": "./node_modules/bower/bin/bower install" diff --git a/src/www/index.html b/src/www/index.html index 6af4c0c..d52c68f 100755 --- a/src/www/index.html +++ b/src/www/index.html @@ -23,28 +23,28 @@ - + - - + + - - + + - + - + - + - + @@ -61,22 +61,22 @@ var less=less||{}; less.env='development'; - + - + - - + + - + diff --git a/src/www/js/app/boot.js b/src/www/js/app/boot.js index 080db56..1354dd6 100644 --- a/src/www/js/app/boot.js +++ b/src/www/js/app/boot.js @@ -1,5 +1,5 @@ require.config({ - baseUrl: 'js', + baseUrl: '/js', paths: { 'es5-shim': '../components/es5-shim/es5-shim', '$': '../components/jquery/jquery', diff --git a/src/www/robots.txt b/src/www/robots.txt new file mode 100644 index 0000000..c3b8430 --- /dev/null +++ b/src/www/robots.txt @@ -0,0 +1 @@ +User-agent: * Disallow: Sitemap: /seo \ No newline at end of file diff --git a/tasks/server/server.js b/tasks/server/server.js index b01fdd7..7d1579f 100755 --- a/tasks/server/server.js +++ b/tasks/server/server.js @@ -3,7 +3,9 @@ module.exports = function(grunt) { /* server.js */ var express = require('express'), - util = require('util'); + util = require('util'), + phantom = require('phantom'), + portscanner = require('portscanner'); var startServer = function(config) { config = config || {}; @@ -74,6 +76,28 @@ module.exports = function(grunt) { server.use(express.errorHandler({dumpExceptions: true, showStack: true})); server.all(apiPrefix + '*', proxyRequest); + + server.all('/seo/*', function(request, response){ + portscanner.findAPortNotInUse(40000, 60000, 'localhost', function(err, freeport) { + phantom.create({'port': freeport}, function(ph){ + return ph.createPage(function(page) { + var url = 'http://' + request.headers.host + '/' + request.originalUrl.replace('/seo/', '#'); + console.log('open', url); + return page.open(url, function(status) { + console.log("opened url for SEO", status); + return page.evaluate((function() { + return document.documentElement.outerHTML; + }), function(result) { + response.status(200); + response.send(result); + return ph.exit(); + }); + }); + }); + }); // ! phantom.create + }); + }); + server.get('/*', function(req, res) { res.redirect(util.format('/#%s#', req.originalUrl)); From b297f1205e4147d783a0e44c12b332210c56cef1 Mon Sep 17 00:00:00 2001 From: Robert Williams Date: Fri, 18 Apr 2014 12:49:01 -0500 Subject: [PATCH 04/14] paths now work at root as opposed to /seo path, fixed output using timeout(500ms) added /test path still need to implement caching and use event listeners instead of timeout. --- src/www/index.html | 2 ++ src/www/js/app/app.js | 3 ++- src/www/js/app/net/HomeController.js | 8 ++++++++ src/www/js/app/ui/views/BaseView.js | 1 + src/www/js/templates/home.html | 3 ++- tasks/server/server.js | 29 ++++++++++++++++++---------- 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/www/index.html b/src/www/index.html index d52c68f..5c71af0 100755 --- a/src/www/index.html +++ b/src/www/index.html @@ -55,6 +55,8 @@
+ + - + @@ -76,10 +79,9 @@ + - - diff --git a/tasks/server/server.js b/tasks/server/server.js index 7d85f45..8ae7fe6 100755 --- a/tasks/server/server.js +++ b/tasks/server/server.js @@ -9,7 +9,8 @@ module.exports = function (grunt) { bodyParser = require('body-parser'), compression = require('compression'), errorhandler = require('errorhandler'), - vhost = require('vhost'); + vhost = require('vhost'), + livereload = require('express-livereload'); var startServer = function (config) { config = config || {}; @@ -151,6 +152,8 @@ module.exports = function (grunt) { listenUp(); } + config.livereload && livereload(server, {watchDir: base}); + function listenUp() { if (domain) { server.use(vhost(domain, server)); @@ -174,7 +177,8 @@ module.exports = function (grunt) { proxyPort: options.proxyPort || '80', proxyProtocol: options.proxyProtocol || 'http', preRender: options.preRender, - compress: options.compress + compress: options.compress, + livereload: options.livereload }), args = this.args, done = args[args.length - 1] === 'watch' ? function () { From 31144ef12b91eb20f9f7ad58d4e653fad1a35424 Mon Sep 17 00:00:00 2001 From: Robert Williams Date: Thu, 8 May 2014 16:56:11 -0500 Subject: [PATCH 12/14] implemented SPDY. --- Gruntfile.js | 9 +++++- package.json | 3 +- tasks/server/server.js | 65 ++++++++++++++++++++++++++++-------------- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 6706d12..c47104c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -221,7 +221,7 @@ module.exports = function( grunt ) { }, prod: { options: { - port: process.env.PORT || 8080, + port: 80, hostname: '0.0.0.0', base: 'build/www', apiPrefix: '/api*', @@ -230,6 +230,13 @@ module.exports = function( grunt ) { cache: { size: 100 } + }, + // Uncommenting will server all traffic through SPDY/HTTPS on default ports. + ssl: { + port: 443, + key: 'keys/server.key', + cert: 'keys/server.crt', + ca: 'keys/server.csr' } } }, diff --git a/package.json b/package.json index 6a38396..b94dd80 100755 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "compression": "^1.0.2", "errorhandler": "^1.0.1", "vhost": "^1.0.0", - "express-livereload": "0.0.24" + "express-livereload": "0.0.24", + "spdy": "^1.25.6" }, "scripts": { "postinstall": "./node_modules/bower/bin/bower install" diff --git a/tasks/server/server.js b/tasks/server/server.js index 8ae7fe6..e553c93 100755 --- a/tasks/server/server.js +++ b/tasks/server/server.js @@ -10,12 +10,14 @@ module.exports = function (grunt) { compression = require('compression'), errorhandler = require('errorhandler'), vhost = require('vhost'), - livereload = require('express-livereload'); + livereload = require('express-livereload'), + spdy = require('spdy'), + fs = require('fs'); var startServer = function (config) { config = config || {}; var requester = require(config.proxyProtocol); - var server = express(); + var app = express(); var hourMs = config.hourMs || 0 * 60 * 60, domain = config.vhost || 'localhost', base = config.base, @@ -75,10 +77,10 @@ module.exports = function (grunt) { req.end(); } - config.compress && server.use(compression()); - server.use(bodyParser()); // TODO: Protect agains exploit: http://andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html - server.use(errorhandler({dumpExceptions: true, showStack: true})); - + config.compress && app.use(compression()); + app.use(bodyParser()); // TODO: Protect agains exploit: http://andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html + app.use(errorhandler({dumpExceptions: true, showStack: true})); + if (config.preRender) { if (config.preRender.cache) { var cache = LRU({ @@ -87,12 +89,11 @@ module.exports = function (grunt) { } phantom.create(function (err, ph) { - server.get('/*', function (request, response) { + app.get('/*', function (request, response) { if ((request.url == '/') || (request.url == '/index.html')) { if (request.headers['user-agent'].indexOf('PhantomJS') !== -1) { response.sendfile(base + '/index.html'); } else { - console.log('trying to get through phantom.'); getThroughPhantom(true); } return; @@ -104,9 +105,8 @@ module.exports = function (grunt) { return; } if (cache) { - var cached = cache.get(request.originalUrl); + var cached = cache.get(request.url); if (cached) { - console.log('response from cache.'); response.status(200); response.send(cached); return; @@ -137,37 +137,58 @@ module.exports = function (grunt) { return page.open(url, function (err, status) { //console.log('open', url, status); + err && console.log('page.open error:', err); }); }); } }); listenUp(); - }); // ! phantom.create + }, {parameters:{'ignore-ssl-errors':'yes'}}); // ! phantom.create } else { // dev mode. - server.use(express.static(base, {maxAge: hourMs})); - server.get('/*', function (req, res) { + app.use(express.static(base, {maxAge: hourMs})); + app.get('/*', function (req, res) { res.redirect(util.format('/#%s#', req.originalUrl)); }); listenUp(); } - config.livereload && livereload(server, {watchDir: base}); + config.livereload && livereload(app, {watchDir: base}); function listenUp() { if (domain) { - server.use(vhost(domain, server)); + app.use(vhost(domain, app)); + } + + if (config.ssl) { + var spdyOptions = { + key: fs.readFileSync(config.ssl.key), + cert: fs.readFileSync(config.ssl.cert), + ca: fs.readFileSync(config.ssl.ca) + }; + var spdyServer = spdy.createServer(spdyOptions, app); + spdyServer.listen(config.ssl.port); + console.log('SPDY server running at %s:%d', domain, config.ssl.port); + // set up a route to redirect http to https + var httpServer = express(); + httpServer.use('*', function(req,res){ + res.redirect('https://' + req.headers.host + (config.ssl.port == 443 ? '' : ':' + config.ssl.port) + req.url) + }); + domain && httpServer.use(vhost(domain, httpServer)); + httpServer.listen(port); + console.log('Express forwarder running at %s:%d', domain, port); + } else { + app.listen(port); + console.log('Express server running at %s:%d', domain, port); } - server.listen(port); - console.log('Express server running at %s:%d', domain, port); } - return server; + return app; }; grunt.registerMultiTask('server', 'Runs a static web and proxy server', function () { var options = this.options({}); - var server = startServer({ + var app = startServer({ host: options.apiBaseUrl, // override this with your third party API host, such as 'search.twitter.com'. hourMs: 0 * 60 * 60, vhost: options.vhost, @@ -178,13 +199,13 @@ module.exports = function (grunt) { proxyProtocol: options.proxyProtocol || 'http', preRender: options.preRender, compress: options.compress, - livereload: options.livereload + livereload: options.livereload, + ssl: options.ssl }), args = this.args, done = args[args.length - 1] === 'watch' ? function () { } : this.async(); - - server.on('close', done); + app.on('close', done); }); }; From 4a2dc774b8163ab8c4ce72f1e5470d9163a426cd Mon Sep 17 00:00:00 2001 From: Robert Williams Date: Mon, 12 May 2014 14:12:48 -0500 Subject: [PATCH 13/14] added hinting. fixed proxy server. --- tasks/server/server.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tasks/server/server.js b/tasks/server/server.js index e553c93..ab18187 100755 --- a/tasks/server/server.js +++ b/tasks/server/server.js @@ -78,7 +78,8 @@ module.exports = function (grunt) { } config.compress && app.use(compression()); - app.use(bodyParser()); // TODO: Protect agains exploit: http://andrewkelley.me/post/do-not-use-bodyparser-with-express-js.html + app.all(apiPrefix + '*', proxyRequest); + app.use(bodyParser()); app.use(errorhandler({dumpExceptions: true, showStack: true})); if (config.preRender) { @@ -119,6 +120,7 @@ module.exports = function (grunt) { page.evaluate((function () { return document.documentElement.outerHTML; }), function (err, result) { + links.length && response.setHeader('Link', links); cache && cache.set(request.originalUrl, result); response.status(200); response.send(result); @@ -133,6 +135,17 @@ module.exports = function (grunt) { outputPage(); } }; + + // Add resource hinting. + var links = []; + page.onResourceReceived = function(resourceRequest) { + if (resourceRequest.id == 1 || resourceRequest.stage != 'end') { + return; + } + links.push('<' + resourceRequest.url + '>; rel=subresource'); +// response.setHeader('Link', '<' + resourceRequest.url + '>; rel=subresource'); + } + var timeout = setTimeout(outputPage.bind(this), 10000); // timeout in 10 seconds return page.open(url, function (err, status) { From 712a0d2b03020ee9476283d5f5bc01d211a941ba Mon Sep 17 00:00:00 2001 From: Robert Williams Date: Wed, 14 May 2014 15:31:06 -0500 Subject: [PATCH 14/14] moved back to port 8080, turned off SSL. --- Gruntfile.js | 16 ++++++++-------- tasks/server/server.js | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index c47104c..4bd13f2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -221,7 +221,7 @@ module.exports = function( grunt ) { }, prod: { options: { - port: 80, + port: process.env.PORT || 8080, hostname: '0.0.0.0', base: 'build/www', apiPrefix: '/api*', @@ -231,13 +231,13 @@ module.exports = function( grunt ) { size: 100 } }, - // Uncommenting will server all traffic through SPDY/HTTPS on default ports. - ssl: { - port: 443, - key: 'keys/server.key', - cert: 'keys/server.crt', - ca: 'keys/server.csr' - } + // Uncommenting will server all traffic through SPDY/HTTPS. +// ssl: { +// port: 443, +// key: 'keys/server.key', +// cert: 'keys/server.crt', +// ca: 'keys/server.csr' +// } } }, doc: { diff --git a/tasks/server/server.js b/tasks/server/server.js index ab18187..aab614b 100755 --- a/tasks/server/server.js +++ b/tasks/server/server.js @@ -143,7 +143,6 @@ module.exports = function (grunt) { return; } links.push('<' + resourceRequest.url + '>; rel=subresource'); -// response.setHeader('Link', '<' + resourceRequest.url + '>; rel=subresource'); } var timeout = setTimeout(outputPage.bind(this), 10000); // timeout in 10 seconds