4

I am trying to setup my Nodejs/Express hosting server to have multiple applications (Sails.js app type) running on my VPS but I got this error :

/srv/data/web/vhosts/default/node_modules/vhost/index.js:78
  throw new TypeError('argument server is unsupported')
        ^
TypeError: argument server is unsupported
   at createHandle (/srv/data/web/vhosts/default/node_modules/vhost/index.js:78:9)
   at vhost (/srv/data/web/vhosts/default/node_modules/vhost/index.js:39:16)
   at Object.<anonymous> (/srv/data/web/vhosts/default/server.js:46:9)
   at Module._compile (module.js:456:26)
   at Object.Module._extensions..js (module.js:474:10)
   at Module.load (module.js:356:32)
   at Function.Module._load (module.js:312:12)
   at Function.Module.runMain (module.js:497:10)
   at startup (node.js:119:16)
   at node.js:906:3

Of course I previously installed all my dependencies.
My Nodejs/Express base configuration for multiple apps is good because it works fine with this express vhosts example configuration:
https://github.com/loicsaintroch/express-vhosts

So here my nodejs server app structure:

.../vhosts/default/server.js
                   package.json
                  /app1
                       /app.js
                  /app2
                       /app.js
                  /app3
                       /app.js

And here my server.js file based on this previous github example:

// Module dependencies
var express = require('express');
var vhost = require('vhost');
var app = express();

// vhosts
app
  .use(vhost('app1.com', require('./app1/app.js')))
  .listen(8080);

And the package.json file:

{
  "name": "default",
  "private": true,
  "version": "0.0.1",
  "description": "Default git repository for some web applications.",
  "dependencies": {
    "express": "^4.2.0",
    "vhost": "^2.0.0",
    "forever": "^0.11.1",
    "static-favicon": "^1.0.0",
    "ejs": "^1.0.0",
    "morgan": "^1.0.0",
    "cookie-parser": "^1.0.1",
    "body-parser": "^1.0.0",
    "debug": "^0.7.4"
  },
  "scripts": {
    "start": "forever start server.js --prod",
    "debug": "node debug server.js"
  },
  "main": "server.js"
}

Error come from vhost npm package:

/**
 * Create handle to server.
 *
 * @param {function|Server} server
 * @return {function}
 * @api private
 */

function createHandle(server){
  if (typeof server === 'function') {
    // callable servers are the handle
    return server
  } else if (typeof server.emit === 'function') {
    // emit request event on server
    return function handle(req, res) {
      server.emit('request', req, res)
    }
  }

  throw new TypeError('argument server is unsupported')
}

OK here precisely I think vhost package has a problem with the app.js response from sails.js framework. Here the app.js file content from my Sails.js app:

/**
 * app.js
 *
 * Use `app.js` to run your app without `sails lift`.
 * To start the server, run: `node app.js`.
 *
 * This is handy in situations where the sails CLI is not relevant or useful.
 *
 * For example:
 *   => `node app.js`
 *   => `forever start app.js`
 *   => `node debug app.js`
 *   => `modulus deploy`
 *   => `heroku scale`
 *
 *
 * The same command-line arguments are supported, e.g.:
 * `node app.js --silent --port=80 --prod`
 */

// Ensure a "sails" can be located:
(function() {
  var sails;
  try {
    sails = require('sails');
  } catch (e) {
    console.error('To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.');
    console.error('To do that, run `npm install sails`');
    console.error('');
    console.error('Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.');
    console.error('When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,');
    console.error('but if it doesn\'t, the app will run with the global sails instead!');
    return;
  }

  // Try to get `rc` dependency
  var rc;
  try {
    rc = require('rc');
  } catch (e0) {
    try {
      rc = require('sails/node_modules/rc');
    } catch (e1) {
      console.error('Could not find dependency: `rc`.');
      console.error('Your `.sailsrc` file(s) will be ignored.');
      console.error('To resolve this, run:');
      console.error('npm install rc --save');
      rc = function () { return {}; };
    }
  }


  // Start server
  sails.lift(rc('sails'));
})();

==============================================

UPDATE: FULL SOLUTION EXAMPLE
As a synthesis of the great answer I wrote a complete case study available here https://github.com/migswd/express-sails-vhosts

==============================================

Miguel Bocquier
  • 2,449
  • 5
  • 22
  • 38

1 Answers1

3

The problem here is that you're trying to shoehorn an example meant for Express apps to work with Sails apps.

If you look at the app.js files from the example vhost apps, they all use module.exports to return an Express app instance. The app.js from the Sails app you posted clearly does no such thing; it doesn't export anything at all. Furthermore, that file is calling sails.lift, which starts its own server listening on port 1337.

A little elbow grease can get this to work. Instead of lifting the Sails app, you can use sails.load which does everything except start listening on a port. This is an asynchronous method, so it'll require a reworking of your server.js as well.

The Sails app.js files become:

var sails = require('sails');
module.exports = function(cb) {
    process.chdir(__dirname);
    sails.load(cb);
};

Every running sails instance exposes its underlying Express app as .hooks.http.app, so in your server.js, use async or something similar to load all of the Sails apps, then hook them up with vhost:

// Module dependencies
var express = require('express');
var vhost = require('vhost');
var app = express();
var async = require('async');

async.auto({

    app1: require('./app1/app.js'),
    app2: require('./app2/app.js'),
    app3: require('./app3/app.js')

}, function doneLoadingApps(err, apps) {

    app
      .use(vhost('app1.io', apps.app1.hooks.http.app))
      .use(vhost('app2.io', apps.app2.hooks.http.app))
      .use(vhost('app3.io', apps.app3.hooks.http.app))
      // Mix in a vanilla Express app as well
      .use(vhost('app4.io', require('./app4/app.js')))
      .listen(8080);

});
sgress454
  • 24,870
  • 4
  • 74
  • 92
  • OK testing your proposal but got error : /srv/data/web/vhosts/default/server.js:10 app ^ ReferenceError: app is not defined at doneLoadingApps (/srv/data/web/vhosts/default/server.js:10:5) at /srv/data/web/vhosts/default/node_modules/async/lib/async.js:454:17 at /srv/data/web/vhosts/default/node_modules/async/lib/async.js:444:17 at Array.forEach (native) at _each .. – Miguel Bocquier Aug 30 '14 at 17:06
  • Sorry, yeah, you still have to make an actual express app. I was just showing the added part. Adjusting my answer to match. – sgress454 Aug 30 '14 at 19:53
  • Great that works for Sails! Just to validate your answer and the complete resolution of the subject I would like to have server.js config launch sails and non-sails (express) vhosts. I linked app1 to the app1 from original example but I got this error : /srv/data/web/vhosts/default/node_modules/express/lib/router/index.js:120 var search = 1 + req.url.indexOf('?'); ^ TypeError: Cannot call method 'indexOf' of undefined at Function.proto.handle (/srv/data/web/vhosts/default/node_modules/express/lib/router/index.js:120:28) – Miguel Bocquier Aug 31 '14 at 13:45
  • Making me do all the work here, eh? You're probably trying to include the vanilla Express app in the `async.auto` block, but that won't work because its **app.js** doesn't return an asynchronous function like the Sails apps do. Just add it exactly like you did in the original example. Updated my example to demonstrate. – sgress454 Aug 31 '14 at 18:58
  • You are the boss man ! :D I was thinking about something like that but could not figure it out. It is not that I m lazy, it is that I am nodejs baby boomer :D Thank you so much ! C U... – Miguel Bocquier Aug 31 '14 at 20:50
  • Hey Guys, I'm trying to use your example (the one from github) on a freshly created bunch of sails apps and getting the following errors: `error: Malformed hook! (moduleloader)` `error: Hooks should be a function with one argument (`sails`)` Any ideas why? – netlander Apr 13 '16 at 15:25
  • @netlander which Github example are you referring to? – sgress454 Apr 19 '16 at 22:10
  • @sgress454 Late I know! The example mentioned in the poster's edit https://github.com/migswd/express-sails-vhosts – netlander Apr 25 '16 at 17:37