Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Support parse.com hosting #14

Closed
wants to merge 4 commits into from

2 participants

@mikepugh

Parse.com Cloud Code hosting provides a great & scalable server for static AngularJS sites. Out of the box, the prerender-node code won't run on their platform. I've modified the code to get it working with Parse.

To get it running, go through the Parse Docs through Dynamic Websites. Add the parse compatible prerender-node JS file into the ./cloud/ folder next to app.js and main.js.

Within app.js, do the following:

var express = require('express');
var app = express();

app.use(require('cloud/prerenderio.js').set('prerenderToken','YOUR_TOKEN'));

// Global app configuration section
app.set('views', 'cloud/views');  // Specify the folder to find templates
app.set('view engine', 'ejs');    // Set the template engine
app.use(express.bodyParser());    // Middleware for reading request body

app.get('/', function(req, res) {
  res.render('hello', {message: 'Some stuff..'});
});

// This is an example of hooking up a request handler with a specific request
// path and HTTP verb using the Express routing API.
app.get('/hello', function(req, res) {
  res.render('hello', { message: 'Congrats, you just set up your app!' });
});

// // Example reading from the request query string of an HTTP get request.
 app.get('/test', function(req, res) {
   // GET http://example.parseapp.com/test?message=hello
   res.send(req.query.message);
 });

// // Example reading from the request body of an HTTP post request.
// app.post('/test', function(req, res) {
//   // POST http://example.parseapp.com/test (with request body "message=hello")
//   res.send(req.body.message);
// });

// Attach the Express app to Cloud Code.
app.listen();

You can't just upload your index.html file into the ./public/ folder and have it served statically because then it'll bypass the express node server with its prerender.io hook.

Instead, your index.html file, in the above example, is stored as ./cloud/views/hello.ejs. This way it will be served up by node and the prerender middleware will run.

Any other static content (angular view HTML files, module JS files, etc) should be stored in ./public/

@thoop
Owner

Build is failing. Looks like you made it only support Parse...

Would this be better as a separate fork you can keep up to date?

@mikepugh

It'd make more sense for prerender.io users if everything remain under the prerender github account. I can update this to dynamically choose between a normal node env vs parse on init.

@mikepugh

Updated the commit to support both Parse and the regular node environment.

index.js
@@ -1,5 +1,12 @@
-var http = require('http')
- , url = require('url');
+
+
+var http = require('http'),
+ url = require('url');
+
+
+// Parse doesn't expose this process object, so create a dummy obj to avoid null refs
+var proc = process || {};
@thoop Owner
thoop added a note

Instead of creating a dummy object, do something like this on init:

if(process) prerender.prerenderToken = process.env.PRERENDER_TOKEN;

And then use this.prerenderToken everywhere else instead of checking both. That will clean up the dummy obj and the checks for those variables everywhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
index.js
((9 lines not shown))
'User-Agent': req.headers['user-agent']
};
}
- http.get(options, function(res) {
-
- var pageData = "";
- res.on('data', function (chunk) {
- pageData += chunk;
+ if(prerender.useParseCloud) {
@thoop Owner
thoop added a note

Abstract this if/else into a method that accepts the options object and conditionally does the right thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@thoop
Owner

Also, write some tests to make sure Parse stuff works and won't break in the future.

@mikepugh

It was getting ugly just trying to hack parse into the main code, so I broke it out and instead provided a setAdaptor() method on the prerender object.

The usage in a Parse Environment would now look like:

var express = require('express');
var app = express();

var parseAdaptor = require('cloud/prerender-parse.js');

app.use(require('cloud/prerenderio.js').setAdaptor(parseAdaptor(Parse)).set('prerenderToken','YOUR_TOKEN'));

// the other express code from initial comment above

The default adaptor is the native http one from the original implementation.

This pattern may prove beneficial as services like Parse become more popular for SPA, and I know Firebase is also working on providing SPA hosting.

I've added two simple tests for the parse adaptor as well.

@thoop
Owner

I'll look over everything when I get some time tomorrow.

Thanks!

@thoop
Owner

Sorry I haven't had the time to look this over yet. I'll try to get to it soon.

@mikepugh

Any updates?

@thoop
Owner

Hey Mike,

I've been dealing with some performance issues with prerender lately so I haven't gotten around to any pull requests that require any actual look over. I still plan on doing it, just haven't had the time. Sorry! I'll keep trying to find some time.

@mikepugh

Oh ok, no worries.

@thoop
Owner

Hey @mikepugh. I found lots of other parse.com repos on github that are separate from their parent repos. Would you want to take this code and fork it into prerender-node-parse? That way you can keep it up to date and everything.

Having the switch between the parse vs node just adds more complexity. I'd like both versions to just do one thing and do it well.

I'll add it to the Prerender and prerender-node readme to point people to your repo for parse.com support.

Let me know. Thanks!

@mikepugh

@thoop - that's fine. I'll throw something up @ https://github.com/mikepugh/prerender-parse sometime this week.

@thoop
Owner

Awesome :)

@thoop
Owner

I saw you moved the repo over, nice!

I'm going to close this pull request then.

Thanks @mikepugh !

@thoop thoop closed this
@mikepugh mikepugh deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 17, 2014
  1. @mikepugh

    Support parse.com hosting

    mikepugh authored
  2. @mikepugh
  3. @mikepugh

    Fix process global var bug

    mikepugh authored
Commits on Feb 18, 2014
  1. @mikepugh

    Separate parse adaptor

    mikepugh authored
This page is out of date. Refresh to see the latest.
View
3  .gitignore
@@ -1 +1,2 @@
-node_modules
+node_modules
+.idea
View
20 adaptors/prerender-parse.js
@@ -0,0 +1,20 @@
+
+var parseAdaptor = module.exports = function(Parse) {
+ return function(options, callback) {
+ Parse.Cloud.httpRequest({
+ url: options.href,
+ headers: options.headers,
+ success: function(res) {
+ res.body = res.text;
+ res.statusCode = res.status;
+ callback(res);
+ },
+ error: function(res) {
+ console.error('Request failed with code ' + res.status);
+ console.error(res);
+ callback(null);
+ }
+ });
+ };
+};
+
View
44 adaptors/test/prerender-parse-tests.js
@@ -0,0 +1,44 @@
+var ParseMock = {
+ Cloud: {
+ config: {},
+ httpRequest: function(options) {
+ config = options;
+ }
+ }
+};
+
+var assert = require('assert')
+ , sinon = require('sinon')
+ , prerender = require('../../index')
+ , parseAdaptor = require('../prerender-parse')
+ , user = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36';
+
+
+describe('Prerender Parse Adaptor', function() {
+ var adaptor = parseAdaptor(ParseMock);
+
+ it('should set parse adaptor', function() {
+ assert.notEqual(prerender.adaptor, adaptor);
+ prerender.setAdaptor(adaptor);
+ assert.equal(prerender.adaptor, adaptor);
+ });
+
+ it('should make parse http call', function() {
+ var req = {
+ method: 'GET',
+ url: '/path?_escaped_fragment_=',
+ headers: { 'user-agent': user },
+ protocol: 'https',
+ get: function(v) {
+ if(v === 'host') return 'google.com';
+ }
+ };
+
+ var spy = sinon.spy(ParseMock.Cloud, "httpRequest");
+ prerender.setAdaptor(adaptor);
+ prerender.getPrerenderedPageResponse(req);
+ assert(spy.called);
+ ParseMock.Cloud.httpRequest.restore();
+ });
+
+});
View
65 index.js
@@ -1,5 +1,8 @@
-var http = require('http')
- , url = require('url');
+
+
+var http = require('http'),
+ url = require('url');
+
var prerender = module.exports = function(req, res, next) {
@@ -24,6 +27,23 @@ var prerender = module.exports = function(req, res, next) {
});
};
+// Helper method to get the prerenderServiceUrl from the env variable.
+prerender.getEnvServiceUrl = function() {
+ if(typeof process !== 'undefined' && typeof process.env !== 'undefined') {
+ return process.env.PRERENDER_SERVICE_URL;
+ }
+ return undefined;
+};
+
+// Can't set prerenderServiceUrl below since it needs to be changed later
+// on in a unit test, after this init code. Created method above to handle
+// scenario.
+if(typeof process !== 'undefined' && typeof process.env !== 'undefined') {
+ prerender.prerenderToken = process.env.PRERENDER_TOKEN;
+}
+
+
+
// googlebot, yahoo, and bingbot are not in this list because
// we support _escaped_fragment_ and want to ensure people aren't
// penalized for cloaking.
@@ -94,7 +114,7 @@ prerender.blacklisted = function(blacklist) {
};
-prerender.shouldShowPrerenderedPage = function(req) {
+prerender.shouldShowPrerenderedPage = function(req) {
var userAgent = req.headers['user-agent']
, isRequestingPrerenderedPage = false;
@@ -128,18 +148,9 @@ prerender.shouldShowPrerenderedPage = function(req) {
return isRequestingPrerenderedPage;
};
-
-prerender.getPrerenderedPageResponse = function(req, callback) {
- var options = url.parse(prerender.buildApiUrl(req));
- if(this.prerenderToken || process.env.PRERENDER_TOKEN) {
- options.headers = {
- 'X-Prerender-Token': this.prerenderToken || process.env.PRERENDER_TOKEN,
- 'User-Agent': req.headers['user-agent']
- };
- }
-
+// Default node http adaptor
+prerender.adaptor = function(options, callback) {
http.get(options, function(res) {
-
var pageData = "";
res.on('data', function (chunk) {
pageData += chunk;
@@ -150,8 +161,28 @@ prerender.getPrerenderedPageResponse = function(req, callback) {
callback(res);
});
}).on('error', function(e) {
- callback(null);
- });
+ callback(null);
+ });
+};
+
+prerender.setAdaptor = function(adaptor) {
+ if(adaptor) {
+ this.adaptor = adaptor;
+ }
+ return this;
+};
+
+prerender.getPrerenderedPageResponse = function(req, callback) {
+ var options = url.parse(prerender.buildApiUrl(req));
+ if(this.prerenderToken) {
+ options.headers = {
+ 'X-Prerender-Token': this.prerenderToken,
+ 'User-Agent': req.headers['user-agent']
+ };
+ }
+
+ this.adaptor(options, callback);
+
};
@@ -170,7 +201,7 @@ prerender.buildApiUrl = function(req) {
prerender.getPrerenderServiceUrl = function() {
- return this.prerenderServiceUrl || process.env.PRERENDER_SERVICE_URL || 'http://service.prerender.io/';
+ return this.prerenderServiceUrl || this.getEnvServiceUrl() || 'http://service.prerender.io/';
};
prerender.beforeRenderFn = function(req, done) {
Something went wrong with that request. Please try again.