Assets not routing properly with NGNIX, Node 8.11 and Angular 6 - javascript

I have an issue getting assets to load properly in our current setup. We use NGINX, Node 8.11, angular 6
In a nut shell, I had to do some parsing of request coming into our node server.js to get files to load properly for angular.
Here is the setup a typical application called heroes:
Nginx
location /heroes/ {
proxy_pass
http://unix:///myapps/tmp/node.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header X-UA-Compatible "IE=edge";
}
Node Server.js
...
//==============================================================
// Point static path to dist
//=================================================================
app.use(express.static(__dirname + '/dist/'));
// set the static files location for the angular build
...
Node Server.js - create an allowed extension list
...
// Allowed extensions list can be extended depending on your own needs
const allowedExt = [
'.js',
'.ico',
'.css',
'.png',
'.jpg',
'.woff2',
'.woff',
'.ttf',
'.svg',
'.map',
'.otf',
'.eot',
'.gif'
];
...
Node Server.js - route files to the angular dist
...
// Redirect all the other requests
// TODO: This part is a hack.
//The main issue is express not serving up the static assets
app.get('*', (req, res) => {
if (allowedExt.filter(ext => req.url.indexOf(ext) > 0).length > 0) {
var iFileStart=req.url.lastIndexOf("/");
var sFile=req.url.substring(iFileStart+1);
res.sendFile(path.resolve('dist/'+sFile));
} else {
res.sendFile(path.resolve('dist/index.html'));
}
});
...
Angular index.html
...
<base href="/heroes/">
...
With this setup - my apps work for the most part. I had to add a few more kludges into it for some other issues.
The problem is - express or my nginx setup wasn't routing the request for assets correctly before this hack. I'm pretty sure I shouldn't have to check file extensions and route them differently.
If I change the Node Server.js file to this:
app.get('*', (req, res) => {
res.sendFile(path.resolve('dist/index.html'));
});
Then I get this error in the browers:
JS files being served as html?
It seems that the files can be found but aren't being processed as JS files.
Any suggestions?

Ok, I figure out my own issue.
Had to edit the following:
NGINX:
location /heroes/ {
root /app/mydir/heroes/dist <--- added this
proxy_pass http://unix:///myapps/tmp/node.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header X-UA-Compatible "IE=edge";
}
Server.js
...
//======================================================================
// Point static path to dist - false will cause a 404 if not found
//========================================================================
app.use(function (req, res, next) {
next();
}, express.static(__dirname+'/dist',{fallthrough:true}));
...
....
app.get('*', (req, res) => {
res.sendFile(path.resolve('dist/heores/index.html'));
});
...
angular.json
"outputPath": "dist/heroes",
Now everything works. Hopefully others will find this useful.

Related

Serving a React app from Node while also having an API

So I am trying to have a Node/React setup on Ubuntu, inside an Nginx server.
The React app works fine, however when I try to have API endpoints (in Node) for the React app to call, those endpoints don't work - neither for the app, nor for going to those endpoints from a browser.
This is what some of the code looks like:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'client/build')));
app.use(express.json());
app.get('/api/contactinfo', async (req, res) => {
let contactinfo = await Information.findAll({
plain: true,
attributes: ["phone", "email", "address"],
});
res.json(contactinfo);
});
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'));
});
const port = process.env.PORT || 5000;
app.listen(port);
So for example, in this part, I might go to the React app's contact page (example.com/contact), and that loads fine. But the API call that the React app makes to the node server fails. So it seems like the React routing is working, but not the Node routing.
Likewise, if I go to just the Node API directly (example.com/api/contactinfo), that fails with a 502 bad gateway.
My Nginx setup looks like this:
location / {
proxy_pass http://localhost:5000;
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've also got some SSL cert setup stuff as well, though I'm not sure if that is relevant.
When I look at the Nginx error.log, this is what I see:
2020/09/02 15:36:54 [error] 1424#1424: *325 upstream prematurely closed connection while reading response header from upstream, client: 35.3.25.220, server: exampledomain.com, request: "GET /api/contactinfo HTTP/1.1", upstream: "http://127.0.0.1:5000/api/contactinfo", host: "exampledomain.com"
What exactly is causing my Node app API endpoints to fail? I've tried increasing the timeout, and several other things and nothing seems to be working - I've been trying to fix this problem for hours, but for some reason, despite the fact that I can successfully get React to load, I can't get any Node endpoints to do so.
How do I fix this?
I don't know the error in your implementation but here how I would do it.
// Nginx
server {
charset utf-8;
listen 80 default_server;
server_name _;
# front-end files
location / {
root /opt/front-end;
try_files $uri /index.html;
}
# node api reverse proxy
location /api/ {
proxy_pass http://localhost:5000/;
}
}
// folder structure
.
opt
+-- front-end
| +-- react app build
+-- back-end
+-- node app
// Node app
...
app.listen(5000);
Based on the article How to Deploy a MEAN Stack App to Amazon EC2
The “problem” is the certbot. If you have a self sign certificate it means that .... depending on how you configúrate certbot, basically now everything runs under port 443 not 80. So if on yow request you have some like `http://www domain com/api/endpoint. You’ll get the error you are getting. What you need to do is to use the https module from node.
import bodyParser from "body-parser";
import express from "express";
import fs from "fs";
import path from "path";
// import https
import https from "https";
import { routes } from "./routes";
import { logger } from "./utils/logger";
// The paths of those files keys will depend on where certbot stored them
const servOptions = {
cert: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/fullchain.pem"),
key: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/privkey.pem"),
ca: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/chain.pem"),
};
/**
* Createas an instance of the framework `fsexpress`.
*
* #returns {import("Express").Express} `express` instance.
*/
logger.info("express::expressApp");
const app: Express = express();
const build = path.join(__dirname, "../html");
app.use(express.static(build));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
app.use("/static", express.static(build));
app.use(`${process.env.ENETO_CURRENT}`, routes());
app.use("*", function (req, res) {
return res.status(200).sendFile(`${build}/index.html`);
})
const secure = https.createServer(servOptions, app);
secure.listen(Number(process.env.PORT), () => {
console.log("servOptions: ", servOptions);
logger.info("APP RUNNING");
});
The issue was that I had a misunderstanding of how Node logging worked under Nginx.
I thought any problems or console logs with the Node setup would be logged to the nginx/error.log.
This was not in fact the case.
The Node setup had another problem, which made trying to access my endpoints crash.
The solution here is better logging that is not dependent on any sort of Nginx logs.
Solution
I just got this problem, and there are a couple of things you need.
First
you still need this
import bodyParser from "body-parser";
import express from "express";
import fs from "fs";
import path from "path";
// import https
import https from "https";
import { routes } from "./routes";
import { logger } from "./utils/logger";
// The paths of those files keys will depend on where certbot stored them
const servOptions = {
cert: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/fullchain.pem"),
key: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/privkey.pem"),
ca: fs.readFileSync("/etc/letsencrypt/live/feikdomain.com/chain.pem"),
};
/**
* Createas an instance of the framework `fsexpress`.
*
* #returns {import("Express").Express} `express` instance.
*/
logger.info("express::expressApp");
const app: Express = express();
const build = path.join(__dirname, "../html");
app.use(express.static(build));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
app.use("/static", express.static(build));
app.use(`${process.env.ENETO_CURRENT}`, routes());
app.use("*", function (req, res) {
return res.status(200).sendFile(`${build}/index.html`);
})
const secure = https.createServer(servOptions, app);
secure.listen(Number(process.env.PORT), () => {
console.log("servOptions: ", servOptions);
logger.info("APP RUNNING");
});
Second
on yow location
location / {
proxy_pass http://localhost:5000;
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;
}
more specific on the proxy_pass http://localhost:5000;
change the http or add an s at the end like this
proxy_pass https://localhost:5000;
location / {
proxy_pass https://localhost:5000;
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;
}

How to send headers in Express [MEAN]

I'm a beginner in Express. So I might've failed to frame the question properly. I have created a MEAN application wherein I've separated my frontend and backened. Frontend runs on port:4200 and server runs on port:3000. I wanted to run both frontend and backend on same port as part of deployment. I'm getting MIME type errors, someone told me that there is some problem with my server environment. Maybe I'm not sending headers properly. Here is my code:
I have mentioned tried solutions in the code itself as <----TRIED THIS
server.js
const express = require('express');
express.static.mime.define({'application/javascript': ['js']}); <----TRIED THIS
const bodyParser = require('body-parser');
const path = require('path');
// express.static.mime.define({'application/javascript': ['js']}); <----TRIED THIS
const api = require('./routes/api');
const PORT = 3000;
const app = express();
app.use(express.static(path.join(__dirname, 'dist')));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/api', api);
app.get('/', function(req, res) {
// res.send('Hello from the server'); <----TRIED THIS
// res.writeHead(200, {'Content-Type': 'text/html'}); <----TRIED THIS
// res.set('Content-Type', 'text/plain'); <----TRIED THIS
// res.setHeader("Content-Type","application/json"); <----TRIED THIS
res.sendFile(path.join(__dirname, 'dist/application/index.html'));
})
app.listen(PORT, function() {
console.log('Server listening on PORT '+PORT);
});
api.js
For instance I'm showing you GET function only
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const db = <my db string>;
const jwt = require('jsonwebtoken');
mongoose.connect(
...
)
function verifyToken(req, res, next) {
...
}
router.get('/myarticles', (req, res) => {
var person="Tanzeel Mirza";
console.log('Get request for tanzeel articles');
Article.find({contributor: person}, (error, article) => {
if(error) {
console.log(error)
}
else {
if(!article) {
res.status(401).send('Invalid email')
}
else if(2>4) {
console.log("test passed");
}
else {
res.json(article);
}
}
})
})
module.exports = router;
But still I'm getting
Loading module from “http://localhost:3000/runtime-xxx.js” was blocked because of a disallowed MIME type (“text/html”).
Loading module from “http://localhost:3000/polyfills-xxx.js” was blocked because of a disallowed MIME type (“text/html”).
Loading module from “http://localhost:3000/main-xxx.js” was blocked because of a disallowed MIME type (“text/html”).
Please correct me.
PS: I asked separate questions for MIME error here. But no answers.
Since your assets are inside dist/application folder, Use app.use(express.static(path.join(__dirname, 'dist/application')));
To match all web app routes, Use app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'dist/application/index.html'));
}).
This a generic route and will be called into action only if express can't find any other routes and always serve index.html. For example any valid /api route will never reach this handler, as there a specific route that handles it.
Final code for server.js
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const api = require('./routes/api');
const PORT = 3000;
const app = express();
app.use(express.static(path.join(__dirname, 'dist/application')));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/api', api);
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'dist/application/index.html'));
})
app.listen(PORT, function() {
console.log('Server listening on PORT '+PORT);
});
A few points to not.
To serve static files, you need not set any headers manually. Express looks up the files in the folder (dist folder in your case) you set as static directory with the express.static middleware function. Express also sets the response headers based on the file extension.
So you don't need express.static.mime.define in your code anymore.
In your case you have defined app.use(express.static(path.join(__dirname, 'dist'))); which listens for static files at dist folder. In this app.use command, you haven't used a mount path which means that all the requests will go through the static middleware. If the middleware finds an asset with the same name, path and extension in dist folder it returns the file, else the request is passed to the other route handlers.
Also, If you are using static middleware, as long as there is an index.html in dist folder (immediate child of dist folder), your route handler for "/" will never get invoked as the response will be served by the middleware.
If you don't have an index html file in dist folder(immediate child of dist), but it's present somewhere in subfolders of dist, and still you need to access it using root path "/", only then you need a route handler for path "/" as below.
app.get("/", function(req, res) {
res.sendFile(path.join(__dirname, "dist/application/index.html"));
});
JS files referred using "./" in dist/application/index.html are referred relative to dist folder itself and NOT dist/application folder.
You can refer this REPL for updated code 👉.
https://repl.it/repls/SoreFearlessNagware
Try below urls
/api/myarticles - Rendered by "/api" route handler
/api/myarticles.js - Rendered by static asset middleware because the file exists in dist/api folder
/ - rendered using "/" route handler and res.sendFile because index.html doesn't exist in dist folder.
/test.js - Rendered using static middleware because file exists in dist folder
Additional links for reference.
https://expressjs.com/en/api.html#res.sendFile
https://expressjs.com/en/starter/static-files.html
1.Build your angular project, either inside or outside the server folder using ng build cmd.
2.To build your project inside server, change the dist-folder path in angular-cli.
3.To change path, either use cli cmd or edit the angular-cli.json file's "outDir": "./location/toYour/dist"
Or by using this cli cmd ng build --output-path=dist/example/
4.Then In your server root file include the static build/dist folder using express.
5.Like this app.use(express.static(path.join( 'your path to static folder')));
6.Now restart your server.

Express.js Router "catchall" load index with any url param

I have a basic project set up with server.js in my root with this code:
app.use('/', express.static(__dirname + '/public/'));
Additionally I have a public folder with index.html, styles and scripts folder in that dir root.
I'd like my web application to send users to index.html regardless of any url paramaters they might have. For example: If a user tries to go to localhost:8888/notarealpage it still loads index.html (without a redirect) so I can still reference the 'notarealpage' in the location.href property.
You can use:
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, 'public/index.html'));
});
This way, it will send your index.html no matter the URL.
Please notice you might have to fine tune the sendFile parameter.
Adding
app.get('*', function (req, res) {
res.sendFile((__dirname + '/public/index.html'));
});
to the end of my routes in server.js did the trick.

Express js getting 404 for all static files in a directory

I have multiple directories for my convenience for my static files.
Some of my static files are in client directory and some dashboard related files are in src directory so now my directory structure is as follows
/
|
client //static files and other stuff
server //server side express, app, controller, models etc
src //other static files
I have two angular apps one in client folder and another in src folder and my server side routes are as follows -
app.route('/app/dashboard-v1').get(function(req,res){
res.sendFile(path.join(__dirname, '../src', 'index.html'));
});
// All undefined asset or api routes should return a 404
app.route('/:url(api|img|src|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
// All other routes should redirect to the index.html
app.route('/*')
.get(function (req, res) {
res.sendFile(path.resolve(app.get('appPath') + '/index.html'));
});
So my first angular app is in app/dashboard-v1 and all other urls are redirected to app2
I am getting all the files in my app2 correctly but I am getting 404 for all other files in my second app.
now If I comment out the
// app.route('/:url(api|img|src|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
I am getting my index.html from second app in all files in first app instead of the 404 error
My express configuration is as follows -
if ('production' === env) {
app.use(favicon(path.join(config.root, 'public', 'favicon.ico')));
app.use(express.static(path.join(config.root, 'public')));
app.set('appPath', path.join(config.root, 'public'));
app.use(morgan('dev'));
} else {
app.use(require('connect-livereload')());
app.use(express.static(path.join(config.root, '.tmp')));
app.use(express.static(path.join(config.root, 'client')));
app.use(express.static(path.join(config.root, 'src')));
app.set('appPath', path.join(config.root, 'client'));
app.use(morgan('dev'));
app.use(errorHandler()); // Error handler - has to be last
}
I am assuming something is wrong with my routes. How do I fix this ?
In my index.html in first app(app/dashboard-v1) I have added the
<base href="/src/" />
and all the links inside my index.html are relative like the following is a block from src/index.html (app/dashboard-v1 url app)-
<script src="vendor/angular/angular-animate/angular-animate.js"></script>
<script src="vendor/angular/angular-cookies/angular-cookies.js"></script>
and when I open the network console in my browser the request that is made to the server is like this -
Request URL:http://localhost:9000/src/vendor/angular/angular-animate/angular-animate.js
to which I am getting a 404 status code in browser console
Do you run the app on the / direcotry of your example?
I think that you ahve to set __dirname as the static folder
app.use('/',express.static(__dirname));
or if you want you can set specific folders likt this:
app.use("/public", express.static(__dirname + "/public"));
app.use("/public2", express.static(__dirname + "/public2"));

Can't use routes in Express.JS with nginx

So I set up nginx in front of Express.JS and all is good, the issue is that say I go to website.com/users, I end up getting a 404 Not Found. Apparently going to website.com is fine, but I guess It's not passing through to routes. Here's my config file for nginx
upstream default {
server 127.0.0.1:3000;
keepalive 8;
}
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name website.com default;
access_log /var/log/nginx/default.log;
return 301 https://website.com$request_uri;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://default/;
proxy_redirect off;
}
}
My users route
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;
You need to change your route to look like this:
/* GET users listing. */
router.get('/users', function(req, res, next) {
res.send('respond with a resource');
});
You might also need to include the port for the express server (3000 is default):
proxy_pass http://default:3000;

Categories

Resources