Node, ExpressJS, vhost - Running Multiple Apps - javascript

I would like to use Node/ExpressJS to serve multiple apps, and I'm coming from an IIS background. The issue I'm having is based around restarting individual apps, without restarting all apps.
In IIS, I would simply click on the app (in the 'sites' list) and click on restart, which would restart only that app, leaving all others up and running.
This is what I have...
Server:
// index.js
require('console.table');
var _ = require('underscore-contrib');
var express = require('express');
var vhost = require('vhost')
var app = express();
var config = require("./config.json");
var websitesStart = function(website){
if (website.live) {
var host = vhost(website.domain, require(website.folder))
app.use(host);
}
}
_.each(config.websites, websitesStart);
app.listen(config.port, function(){
console.log('Web Server Running, Port:' + config.port);
console.table(config.websites);
});
Config:
// config.json
{
"port": "8080",
"websites": [
{
"name": "App 1",
"live": "true",
"domain": "app1.uk",
"folder": "../apps/app1"
},{
"name": "App 2",
"live": "true",
"domain": "app2.uk",
"folder": "../apps/app2"
},{
"name": "App 3",
"live": "true",
"domain": "app3.uk",
"folder": "../apps/app3"
}
]
}
Running node index.js starts all my apps just fine.
If I make a change to some code in App 2 for example, how would I restart only that app..? Leaving apps 1 and 3 running..?
UPDATE: I'm running on a Windows OS, so PM2 isn't really an option as it's only in beta.
UPDATE 2 I would really like to run all the apps on port 80, like I can in IIS. Wouldn't I need to use different ports if I ran each app as a different ExpressJS app (running them as separate processes/services)..?

You can use PM2 module for process management.
Just describe your apps in package.json and start them all like:
./node_modules/.bin/pm2 start package.json
After you can use this to restart one app:
./node_modules/.bin/pm2 restart <app_name|id|'all'|json_conf>
So you don't need "websitesStart" function at all.

Related

How to implement Express + Node JS + Browserify properly?

So I'm somewhat new to the whole web development thing with node.js and I'm wondering if someone could help me out with understanding how to implement my application correctly.
So the app is a simple landing page with an email form that takes an email and sends it to the API. I designed this functionality without issue except when I launched my website i'm getting a required not defined error.
I understand that this is because node.js is a server side technology so that when the application goes live, the client doesn't understand what required means.
Through further research, I figured out that I had two options and that's to either implement synchronous dependencies via something like Browserify or take things asynchronously and use something like RequireJS.
Right now I've decided on using Browserify, (unless someone can convince me otherwise) I just need help with figuring out how to implement it for my specific app.
app.js
//The dependenices my node js app needs (also where the require bug occurs)
//------------------------------------------------------------------------------------------------------------------------
const express = require('express'); //Require the express package that we installed with npm install express
const request = require('request'); //Require the express package that we installed with npm install request
const bodyParser = require('body-parser'); //Require the express package that we installed with npm install bodyParser
const path = require('path'); //Require the express package that we installed with npm install path
//------------------------------------------------------------------------------------------------------------------------
const app = express(); //Creates the application with express
//Middleware
app.use(express.json()); //Tells express to use JSON as it's input
app.use(bodyParser.urlencoded({ extended: false })); //Prevents XSS, only will return if the url has specified enconding
app.use(express.static(path.join(__dirname, '/site'))); //Tells the app to use the current path D:\Gaming Galaxy\Website\Website\main\site as the dir for the website
console.log("The directory used is", express.static(path.join(__dirname, '/site')));
app.post('/subscribe', (req, res) => { //Makes a post request to express req is the request and res is the response
const { email, js } = req.body; //Create a constant that makes up of the request's body
const mcData = { //Create a constant JSON object mcData, that contains the email from the above constant and a status message
members: [
{
email_address: email,
status: 'pending'
}
]
}
const mcDataPost = JSON.stringify(mcData); //Turns the JSON object into a string
const options = { //Sets a JSON object of a bunch of options that mailchimp will use
url: 'https://us20.api.mailchimp.com/3.0/lists/f10300bacb',
method: 'POST',
headers: {
Authorization: 'auth f24c3169da044653d1437842e39bece5-us20'
},
body: mcDataPost
}
if (email) { //If there's an email that exists
request(options, (err, response, body) => { //Send a request to mail chimp
if (err) { //If there's an error
res.json({ error: err }) //Print said error
} else { //If there's not an error
if (js) { //If javascript is enabled (boolean)
res.sendStatus(200); //Send a success message
} else {
res.redirect('/success.html'); //If it's disabled, send them to a successful HTML page.
}
}
})
} else {
res.status(404).send({ message: 'Failed' }) //If the email doesn't exist, have it fail
}
});
app.listen(5000, console.log('Server started!')) //Console log that confirms the start of the server
package.json
{
"name": "gaminggalaxy",
"version": "1.0.0",
"main": "site/js/app.js",
"dependencies": {
"body-parser": "^1.19.0",
"commonjs": "^0.0.1",
"express": "^4.17.1",
"index": "^0.4.0",
"node-fetch": "^2.6.6",
"prototype": "^0.0.5",
"request": "^2.65.0",
"requirejs": "^2.3.6",
"uniq": "^1.0.1"
},
"devDependencies": {
"nodemon": "^2.0.15"
},
"scripts": {
"serve": "node app",
"dev": "nodemon app"
},
"keywords": [],
"author": "",
"license": "ISC",
"repository": {
"type": "git",
"url": "git+https://github.com/InvertedTNT/Main.git"
},
"bugs": {
"url": "https://github.com/InvertedTNT/Main/issues"
},
"homepage": "https://github.com/InvertedTNT/Main#readme",
"description": ""
}
index.html (the form itself)
<form action="/subscribe" method="POST">
<div class="newsletter-form-grp">
<i class="far fa-envelope"></i>
<input type="email" name="email" id="email"
placeholder="Enter your email..." required>
</div>
<button id="cta">SUBSCRIBE <i class="fas fa-paper-plane"></i></button>
</form>
folder structure
node_modules
site
-index.html
-css
-js
- app.js
-images
app.js
package-lock.json
package.json
Thank you for your help, I would appreciate any sort of advice on how I can use those dependencies and the overall implementation of browserify.
A browser is an HTTP client.
Express is a framework for building HTTP servers.
HTTP clients make requests to HTTP servers which then send responses back.
Express depends on Node.js. It requires features provided by Node.js (like the ability to listen for network requests) which are not available in browsers.
Browserify can bundle up JavaScript which is written using modules into non-module code that can run in a browser but only if that code does not depend on Node.js-specific features. (i.e. if the JS modules are either pure JS or depend on browser-specific features).
Browserify cannot make Express run inside the browser.
When you run your JS program using Node.js you can then type the URL to the server the program creates into the browser’s address bar to connect to it.

multiple vhosts working on Chrome, but not Firefox or Safari

I’m using express and vhost to set up multiple servers on the same port, each with a different subdomain. Each server corresponds to a local directory on my file system. They only need to serve static files.
~/repos/server/app.js:
const fs = require('fs')
const path = require('path')
const express = require('express')
const vhost = require('vhost')
const app = express()
const PORT = 9000
const virtual_hosts = require('./virtual-hosts.json') // see below
app.use(express.static(path.join(__dirname, '../')))
virtual_hosts.forEach((vh) => {
var vh_app = express()
vh_app.use(express.static(path.join(__dirname, '../', vh.path)))
app.use(vhost(vh.domain, vh_app))
})
app.listen(PORT, () => {
console.log(`
Listening at http://localhost:${PORT}/
Press ctrl + c to stop.
`)
console.log('...')
})
~/repos/server/virtual-hosts.json:
[
{ "domain": "repo1.localhost", "path": "./repo1/" },
{ "domain": "repo2.localhost", "path": "./repo2/" },
{ "domain": "repo3.localhost", "path": "./repo3/" }
]
After running node ~/repos/server/app.js, the url http://localhost:9000/repo1/index.html works in all 3 browsers, but http://repo1.localhost:9000/index.html only works in Chrome, but not Firefox or Safari.
Is there something wrong with my code, or do I need to change some browser settings?
It's a little bit late, but for someone who come up with the same question.
You can't use subdomains on windows of "localhost". I dont know how chrome manage to handle it, but the normal behaviour is that you can't do that.
If you still want to archieve this, have a look at this.

Issue with Cluster on Visual studio code (F5) on Node.js v6.0.0

I have some issue with Visual studio code with Cluster
Edit
If I hit Ctrl + F5 it works correctly, what it's doing other than just F5, do I need to start command always with Ctrl?
---
It seems like workers never starts when started with VS Code Launch command (F5). Do I need to do some changes to .vscode/launch.json file to make Cluster work proberly.
Actual code is copied from Node.js 6 api https://nodejs.org/api/cluster.html#cluster_cluster
npm test Windows Command prompt shows this:
Master started
Listening port 80
Listening port 80
Listening port 80
Listening port 80
VS Code (F5) Debug Console show this:
node --debug-brk=7601 --nolazy index.js
Debugger listening on port 7601
Master started
Debugger listening on port 7602
Debugger listening on port 7603
Debugger listening on port 7604
Debugger listening on port 7605
VS Code launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/index.js",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}",
..........
index.js
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
console.log('Master started')
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(80);
console.log('Listening port 80')
}
I had the same issue. The 2nd workaround described by weinand in https://github.com/Microsoft/vscode/issues/3201 works for me:
Launch node from a terminal and attach to it with the VS Code
debugger.
Run in terminal: node --debug app.js
Then select the default 'attach' launch config and
attach to it.
The workaround is the preferred way if you actually want to debug any
worker and not just the first process that is launched.
I have the same problem on Visual studio code with Cluster.
I found that there are some dirty method to make it works.
Mac OS X:
/Applications/Visual Studio
Code.app/Contents/Resources/app/extensions/node-debug/out/node/nodeDebug.js
Windows:
C:\Program Files (x86)\Microsoft VS Code\resources\app\extensions\node-debug\out\node\nodeDebug.js
Change this
if (!this._noDebug) {
launchArgs.push("--debug-brk=" + port);
}
to
if (!this._noDebug) {
launchArgs.push("--debug=" + port);
}
I know it is not the best way to solve it, but it is working so far for me.

Deploy OpenShift Node.js application from github

I created a node.js (with express) application and have been unsucessfully trying to deploy to OpenShift from github. I am attempting to deploy from the web interface (providing the URL to the github repository root and "master" in the branch/tag field) and am getting an error I'm having trouble understanding:
The initial build for the application failed:
Shell command '/sbin/runuser -s /bin/sh 5724c3b42d5271363b000191 -c "exec /usr/bin/runcon 'unconfined_u:system_r:openshift_t:s0:c4,c687' /bin/sh -c \"gear postreceive --init >> /tmp/initial-build.log 2>&1\""' returned an error. rc=255 .
Last 10 kB of build output: Stopping NodeJS cartridge
Repairing links for 1 deployments
Building git ref 'master', commit a5ca0f7
Building NodeJS cartridge Preparing build for deployment
Deployment id is c2527992
Activating deployment
Starting NodeJS cartridge Sat Apr 30 2016 10:41:09 GMT-0400 (EDT):
Starting application 'profile' ...
Script = server.js
Script Args = Node Options = !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! It is highly recommended that you add a package.json file to your application. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Waiting for application port (8080) become available ...
Application 'profile' failed to start (port 8080 not available) -------------------------
Git Post-Receive Result: failure
Activation status: failure
Activation failed for the following gears: 5724c3b42d5271363b000191
(Error activating gear: CLIENT_ERROR: Failed to execute: 'control start' for /var/lib/openshift/5724c3b42d5271363b000191/nodejs #<IO:0x000000019b0298> #<IO:0x000000019b0220> )
Deployment completed with status: failure postreceive failed
I read a couple of posts about some errors above like port 8080 not available and failed to execute control start but the directives I was able to follow did not solve my issue. I am finding the line that says "using a package.json file is highly recommended" strange as I do have one. My package.json file is:
{
"name": "Portfolio_Memoria",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node server.js"
},
"main": "server.js",
"description": "Portfolio_Memoria",
"author": {
"name": "gorra",
"email": ""
},
"dependencies": {
"express": "~4.9.0",
"body-parser": "~1.8.1",
"cookie-parser": "~1.3.3",
"morgan": "~1.3.0",
"serve-favicon": "~2.1.3",
"debug": "~2.0.0",
"jade": "~1.6.0",
"stylus": "0.42.3"
}
}
And server.js file is:
#!/usr/bin/env node
var debug = require('debug')('Portfolio_Memoria');
var app = require('./app');
if(typeof process.env.OPENSHIFT_NODEJS_PORT === 'undefined'){
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() {
debug('Express server listening on port ' + server.address().port);
});
} else {
app.set('port', process.env.OPENSHIFT_NODEJS_PORT || 3000);
app.set('ip', process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1');
var server = app.listen(app.get('port'), app.get('ip'), function() {
debug('Express server listening on port ' + server.address().port);
});
}
Of course, the application runs without issue locally. I don't know what I am missing here.
EDIT:
I got it to work by creating a blank application in OpenShift, cloning the repository OpenShift creates via command line, copying my whole project to it and pushing it back. This is a workaround and not a solution to the original problem, though.

Yeoman to use google app engine server

I setup Yeoman 1.0 beta to handle my js/css tasks. Everything works fine that, if I run grunt server, it starts up a static server and connects a browser session to port 9000 (livereload). js/css concat, minification are also working.
Now, is there a way I can make it to connect to a google app engine development server (instead of starting a static server). The server is running at port 8080 on localhost, and I want grunt to reload the webpage upon css/js files under watch. These files would be served by GAE server.
I see a section rolling your own at grunt-contrib-connect documentation, but not sure it means an external server. As far as I see, these are the relavent configuration from Gruntfile.js
connect: {
livereload: {
options: {
port: 8080, //*** was 9001 originally **
middleware: function (connect) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app)
];
}
}
},
When I change the port number to 8080, and try to start, obviously it gives error.
Fatal error: Port 8080 is already in use by another process.
so, I don't want to start a new server, but connect through GAE server already running.
Thanks.
In order to use GAE server instead of nodejs server, we need to do the following.
* Compile your less/coffeescript, concat[, minify], copy your code to the location where the app engine code resides.
* Create a task in grunt.js to spawn a shell command to run app engine.
This is the example, that I used as reference. https://github.com/cowboy/grunt/tree/master/tasks
Following grunt.js file may help!
module.exports = function(grunt) {
grunt.initConfig({
....
});
grunt.registerTask('appengine-update', 'Upload to App Engine.', function() {
var spawn = require('child_process').spawn;
var PIPE = {stdio: 'inherit'};
var done = this.async();
spawn('appcfg.py', ['update', 'build/task-manager-angular'], PIPE).on('exit', function(status) {
done(status === 0);
});
});
grunt.registerTask('clean', 'Clean the whole build directory.', function() {
require('child_process').exec('rm -rdf build', this.async());
});
grunt.registerTask('run', 'Run app server.', function() {
var spawn = require('child_process').spawn;
var PIPE = {stdio: 'inherit'};
var done = this.async();
spawn('dev_appserver.py', ['.'], PIPE).on('exit', function(status) {
done(status === 0);
});
});
});
//....
//Other settings
//....
grunt.loadTasks('tasks');
grunt.loadNpmTasks('grunt-coffeelint');
grunt.registerTask('build', 'coffee less concat');
grunt.registerTask('deploy', 'coffee less concat build appengine-update');
grunt.registerTask('default', 'coffee less');
};
Found this Google App Engine management plugin for Grunt

Categories

Resources