Can we use NGINX as webapp for template engine - node.js

I have a requirement for basic html template webapp such as:
http://localhost:3000/myapp?param1=hello&param2=John is called it should return text/html response which looks like this:
<html>
<body>
<p>Nice to see you John. Platform greets you "hello".</p>
</body>
</html>
the name and greeting is templated from param. so template is something like this:
<html>
<body>
<p>Nice to see you {{param1}}. Platform greets you "{{param2}}".</p>
</body>
</html>
I have currently done this in node server using express.js and then the server is exposed publicly via nginx.conf:
server {
listen 80;
# server_name example.com;
location / {
proxy_pass http://private_ip_address:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I was wondering if this could be possible with some plugins or other configuration with bare nginx without hosting the node server on 3000 port.

I was able to solve this using only Nginx to program it using OpenResty's lua module.
The https://github.com/openresty/lua-nginx-module gives ability to program in nginx.conf, where one could use the existing lua libraries such as https://github.com/bungle/lua-resty-template for templating!
myapp.lua:
local template = require("resty.template")
local template_string = ngx.location.capture("/templates/greet.html")
template.render(template_string.body, {
param1 = ngx.req.get_uri_args()["param1"],
param2 = ngx.req.get_uri_args()["param2"]
})
greet.html:
<html>
<body>
<p>Nice to see you {{param1}}. Platform greets you "{{param2}}".</p>
</body>
</html>
nginx.conf:
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
root ./;
server {
listen 8090;
location /myapp {
default_type text/html;
content_by_lua_file ./lua/myapp.lua;
}
}
content_by_lua_file is where the power of openresty comes.
I described the complete process here: https://yogin16.github.io/2018/03/04/nginx-template-engine/
Hopefully, someone would find this helpful.

You can't render file with nginx.
Just send the file with nginx and the rewrite directive then inside the file just include some javascript to replace text content with query parameters
Nginx conf :
location / {
rewrite ^ /static/index.html break;
}
index.html:
<div>My content <span id="name"></span></div>
<script>
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
document.getElementById("name").textContent = getParameterByName("foo"):
</script>

Related

How to serve static files (CSS, ...) with multiples Express app + NGINX as reverse proxy server

Context
I'm runnig multiples nodesJS/Express app on the same server with the same IP adress.
I use Nginx to reverse proxy those apps and redirect it to subfolder adress (and not subdomain, i don't want to).
ex : http://123.0.0.1:8000 => http://monsite.com/Site1
Problem
My assets files (css, images, ...) do not load, I have a 404 error on those static files when the page loads. It happens only when I access the site via the proxy redirect http://monsite.com/Site1 and not when I use the IP adress : http://123.0.0.1:8000
I don't have this problem if a use the reverse proxy location from the root in the nginx conf :
location / {
but I want to access the site from a subfolder adress
My integration
Tree files:
var/www/html
|Site1/
| |server.js
| |Views/
| | |index.pug
| |Public/
| | |Css/
| | | |Style.css
|Site2/
|....
nodejs server code
const PORT = 8000;
const HOSTNAME = 'www.monsite.com';
// Dependencies.
const express = require('express');
const http = require('http');
// Initialization.
var app = express();
var server = http.Server(app);
app.set('port', PORT);
app.set('view engine', 'pug');
app.set('views','Views');
app.use(express.static('Public'));
app.use('/', (request, response) => {
response.render('index');
});
server.listen(PORT, HOSTNAME, function() {
console.log(`STARTING SERVER ON PORT ${PORT}`);
});
index pug code
doctype html
html
head
title Site 1
link(rel="stylesheet" href="/Css/style.css")
body
p Hello
nginx conf
server {
listen 80;
listen [::]:80;
root /var/www/html;
index index.html index.htm index.nginx-debian.html index.php;
server_name www.monsite.com;
location / {
#Reserved for another site
}
location /Site1/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_pass http://123.0.0.1:8000/;
}
}
PS : I tried almost all the solutions and code I found searching for this problem and nothing worked, that's why I'm asking directly here. Thank you.
I think the issue is with the url in the link tag to load the css, the url is invalid because the url is actually /Site1/Css/style.css.

how to make Nodejs redirect work when called via Vuejs

I have created and tested the POST and GET request methods in Nodejs such that I can send the user through the Gocardless sign-up API perfectly fine.
This is a sign-up form provided by their API which allows them to input their details and then returns the user back after they fill it in.
But when I set up a front-end using Vuejs and make the same calls previously made from the back end using Axios, it seems that because the "redirect_url" fed back to me from the GC API had previously been fed directly into the browser url before, now, because it seems vue-router has control of the browser, I'm getting a cross origin error.
How can I configure the files to have the Nodejs back end acting as if it had control of the browser?
The end points are described here:
https://developer.gocardless.com/api-reference/#core-endpoints-redirect-flows
My nginx default is:
server {
charset UTF-8;
listen 80;
root /srv/www/sitename/;
index index.html;
server_name sitename.com;
location /api/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://sitename.com:8081;
proxy_ssl_session_reuse off;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location / {
try_files $uri.html $uri $uri/ /index.html;
}
My button from Vuejs front end:
completeOrder()
..and uses axios this way:
import axios from 'axios'
export default() => {
return axios.create({
baseURL: 'http://sitename.com:8081/api'
})
}
And set it up here:
import Api from '#/services/Api'
export default {
completeOrder () {
return Api().post('order')
}
}
In the back end it sends:
app.post('/api/order', function (req, res){
rp({
//rp is npm request-promise
uri: BASE_URL + "/redirect_flows",
method: 'POST',
body:JSON.stringify(input_json),
headers: headers
}) // this works and API sends me the response
.then(function(response) {
var str_response =JSON.parse(response);
url = str_response['redirect_flows']['redirect_url']
// url works fine when I paste into a separate browser
res.redirect(url)
})
.catch(function (err) {
console.error(err);
})
})
Everything works up until the point:
res.redirect(url)
..where the Gocardless API response supplies me with the URL which I need to load into a browser.
It looks something like this:
https://pay-sandbox.gocardless.com/flow/RE000156
I think I need to break out of Vuejs's control of the browser via vue-router just long enough to allow the user to call the form with the redirect_url, then come back to the home page of the app again.
Any ideas very welcome!
I think you actually have a JS error. In the then block you instantiate a response, but you use a res variable to redirect.
Try chaging the variable
.then(function(response) {
var str_response = JSON.parse(response);
url = str_response['redirect_flows']['redirect_url']
// url works fine when I paste into a separate browser
response.redirect(url)
})
I am not a Vue.JS expert, so I don't know if that works, try using a vanilla JS redirect to test this feature:
window.location.href = url;
This way, you will be sure that the url works. After that, try checking out a full Vue.JS option.

Multiple node.js projects with one domain name, each using path '/' as base url. Nginx & Express.js

I am successfully able to reverse proxy multiple node.js projects to different ports as in the Nginx set up below. What I am trying to accomplish however, is viewing the url as the root when it gets to the Node.js server.
For example, When someone goes to mydomain.com/projects/music_player, is it possible to have the Express application view the request url as just '/' instead of '/projects/music_player'.
My current Nginx setup, and Express configuration example are as follows.
Nginx:
server {
listen 80;
server_name mydomain_name.com;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /projects/music_player/ {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I want to do this:
app.get("/", function (req, res) {
res.sendFile("index.html");
});
app.listen(8000);
Instead of this:
app.get("/projects/music_player", function (req, res) {
res.sendFile("index.html");
});
app.listen(8000);
I am not sure if that is even possible. The reason is that I would like each of my node node.js/express applications to be deployable as standalone applications, without restructuring the code. I am trying to avoid having a bunch of domain names for each project, plus I think it would be pretty cool.
Yes! Of course you can.
I think the best way to do this is to rewrite your entire program into one express app, but you say you want to avoid this.
Then I think the short way with your current setup is to make each program listen to a different port and then map routes to them in your nginx.
For example:
dog.js
app.get("/", function (req, res) {
res.sendFile("dog.html");
});
app.listen(8001);
cat.js
app.get("/", function (req, res) {
res.sendFile("cat.html");
});
app.listen(8002);
And finally in your nginx config file:
server {
listen 80;
server_name mydomain_name.com;
location / {
proxy_pass http://localhost:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /cat/ {
rewrite /cat/ / break;
proxy_pass http://localhost:8002;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Notice the rewrite directive, this prevents for the /cat/ location from being sent to your cat.js app that only expects the / route.
I ran into this same problem, where I didn't want to fully restructure my backend services while wanting to change the endpoint to match others.
I ended up using node-http-proxy with http-proxy-rules. Grabbing an example from the http-proxy-rules github README, you're solution would resemble the following.
var http = require('http'),
httpProxy = require('http-proxy'),
HttpProxyRules = require('http-proxy-rules');
// Set up proxy rules instance
var proxyRules = new HttpProxyRules({
rules: {
'.*/test': 'http://localhost:8080/cool', // Rule (1)
'.*/test2/': 'http://localhost:8080/cool2/' // Rule (2)
},
default: 'http://localhost:8080' // default target
});
// Create reverse proxy instance
var proxy = httpProxy.createProxy();
// Create http server that leverages reverse proxy instance
// and proxy rules to proxy requests to different targets
http.createServer(function(req, res) {
// a match method is exposed on the proxy rules instance
// to test a request to see if it matches against one of the specified rules
var target = proxyRules.match(req);
if (target) {
return proxy.web(req, res, {
target: target
});
}
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('The request url and path did not match any of the listed rules!');
}).listen(6010, cb);
Happy hacking!

node.js: route request to different port on same host

I have a host computer which serves a number of webapplications (not node.js based). It does this using different ports. This means that for example the following applications are live:
app1: http://hostname:3000
app2: http://hostname:3001
app3: http://hostname:3003
Next to that I have a node.js based webapp (running on port 80) which I want to use as a sort of router. When someone navigates to http://localhost/app/app1. I want it to navigate to http://hostname:3000. This is relatively straightforward using a simple redirect. However, I would want to preserve the url http://localhost/app/app1. Can someone point me to a way to make this work using node.js/express?
My routing logic looks somewhat like this (pseudo-code).
app.route('/app/:appName')
.get(appEngine.gotoApp);
appEngine.gotoApp = function(req, res) {
redirectToApp logic
}
You probably better use Nginx setting up a reverse proxy with different locations per application.
It's not what you ask for because it does not use node.js, but if it's the only purpose, Nginx really suits your needs.
For example a Nginx configuration file like should work the way you want :
server {
listen 80;
server_name myapp.com;
location /app1 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /app2 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /app3 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
If you use express, you can try to create the app with the cli express application generator.
It creates an express app and returns it with module exports.
In the server.js file it pass to listen function of the server instance the express app object.
You can create more server object and listen different app with different port.
var server = http.createServer(app);
server.listen(port);
var server2 = http.createServer(app2);
server2.listen(port2);
If you want to point different app based on the url, you can instance an express router instead of express object.
var app1 = express.Router();
Then you can set all your routes into this object with classic get or post or other methods.
Now you are able to pass the router as a middleware of your main express app.
app.use( "app1/", app1 );
You can also pass an express app to middleware, instead of router object, in order to gain the possibility of exec the app with a different url and port server listening.
There is a nice http-proxy lib designed exactly for that!
const httpProxy = require('http-proxy');
const url = require('url');
const proxy = httpProxy.createProxy();
const options = {
'/app/app1': 'http://localhost:3000',
'/app/app2': 'http://localhost:3001',
'/app/app3': 'http://localhost:3003',
}
require('http').createServer((req, res) => {
const pathname = url.parse(req.url).pathname;
for (const [pattern, target] of Object.entries(options)) {
if (pathname === pattern ||
pathname.startsWith(pattern + '/')
) {
proxy.web(req, res, {target});
}
}
}).listen(80);

Unexpected response code: 502 nginx

i have the following setup:
nginx server running as a proxy:
server {
server_name domain.tld;
location / {
# For Read Requests
proxy_pass http://XXX.XXX.XXX.XXX;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~* "(/config.json|/nowjs/now.js|socket.io|/stat/1.gif)" {
proxy_pass http://127.0.0.1:9080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
where XXX.XXX.XXX.XXX is the IP domain for my static content pages and assets.
i have a simple chat application running on same server as the nginx on port 9080
in my index.html page, i have this code:
<div id="chat">
<div id="messages"></div>
<input type="text" id="text-input" />
<input type="button" value="Send" id="send-button" />
</div>
<script type="text/javascript" src="http://domain.tld/nowjs/now.js"></script>
<script type="text/javascript">
$(document).ready(function(){
now.receiveMessage = function(name, message){
$("#messages").append('<br />' + '' + name + '' + ': ' + message);
}
$("#send-button").click(function(){
now.distributeMessage($("#text-input").val());
$("#text-input").val("");
});
now.name = "${name}";
});
</script>
and here is the app.js file:
var server = require('http').createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Chat Server\n');
});
server.listen(9080);
var nowjs = require("now");
var everyone = nowjs.initialize(server);
everyone.now.distributeMessage = function(message){
console.log('User '+this.now.name+' added message ' +message);
everyone.now.receiveMessage(this.now.name, message);
};
everything seems to be running ok, although i keep getting the Unexpected response code: 502 when the page loads up!
is there a way to enable the web proxy for nginx, as looking further using Firefox Firebug, i get this error:
Firefox can't establish a connection to the server at ws://domain.tld/socket.io/1/websocket/-WFvaQTvzASDBMHNpUGA.
any advise much appreciated.
i changed:
var everyone = nowjs.initialize(server);
to
var everyone = nowjs.initialize(server, {socketio: {transports: ["xhr-polling"]}});
seems to have done the job.

Resources