ExpressJs Serve multiple static folders not working - node.js

I want to structure my public folder like this:
- public
- frontend
- backend
- assets # to serve common css, and js static files for backend/frontend
if the request is prefixed by /admin then I want to serve backend folder, else then the frontend folder
this is what I've tried
app.use(function(req, res, next) {
if (req.url.match('^\/admin')) {
express.static(__dirname + '/public/admin')(req, res, next);
} else {
express.static(__dirname + '/public/frontend')(req, res, next);
}
});
app.use(express.static(__dirname + '/public/assets'));
app.get('*', function(req, res, next) {
if (req.url.match('^\/admin')) {
res.sendFile(__dirname + '/public/admin/app/views/index.html');
} else {
res.sendFile(__dirname + '/public/frontend/app/views/index.html');
}
});
but I can't get the static files from the assets folder, it keeps giving me index.html instead.
what is the right approach to make it works ? thx alot

If I understood your question correctly, I think something like this would be more the way to go.
app.use('/admin', express.static('public/backend'));
app.use('/', express.static('public/frontend'));

You can do this way
app.get('/*', function(req, res) {
if(req.path.indexOf('/admin/')!==-1)
res.sendFile(path.join(__dirname, 'public/admin', 'index.html'));
}else {
res.sendFile(path.join(__dirname, 'public/frontend', 'index.html'));
}
});
You can similarly add other urls as well.

Related

Express.js serve static folder at specific url only

I have an existing express app I'm using for an API but I also want to deliver a folder with an HTML file and some images/css/js. I can get it to serve the HTML page but it does so at / while I only want it served at /manual. Here is my stripped down code with just express.static, my / route and the catch all redirect to /
const app = express();
app.use(express.static('manual'));
app.get('/', (req, res) => {
res.status(403);
res.send('Access denied.');
return;
});
app.get('*', (req, res) => {
res.redirect(301, '/');
return;
});
What currently happens: Going to mysite.com/manual redirects to mysite.com/ which serves the index.html in the manual folder.
What I want to happen: Going to mysite.com/manual serves the index.html in the manual folder. And going to mysite.com/ throws a 403 status code.
Middleware/handlers are applied in the order you declare them, so you just need to change the order
const app = express();
app.get('/', (req, res) => {
res.status(403);
res.send('Access denied.');
return;
});
// adding a first parameter lets you apply the middleware to a route
app.use('/manual', express.static('manual'));
app.get('*', (req, res) => {
res.redirect(301, '/');
return;
});

express.static handling root url request

express.static is handling the root url request.
e.g. I want to do a redirect in express from https://example.com to https://example.com/dashboard.
Check the cases below, first one works, second does not. I expect the second to work too. Anyone knows why?
Case 1 (works)
app.get('/', (req, res, next) => {
res.redirect('/dashboard');
})
app.use(express.static(path.join(__dirname, 'dist')))
app.get('/dashboard', (req, res, next) => {
//do stuff
})
Case 2 (does not work for me)
app.use(express.static(path.join(__dirname, 'dist')))
//request doesn't come here
app.get('/', (req, res, next) => {
res.redirect('/dashboard')
})
app.get('/dashboard', (req, res, next) => {
//do some stuff
})
That would happen if there's a file dist/index.html, because that's what express.static() would look for when retrieving a directory (in this case /).
You can turn that behaviour off like this:
app.use(express.static(path.join(__dirname, 'dist'), { index : false }))
Documented here: http://expressjs.com/en/4x/api.html#express.static

Express.js: How to serve one file for the default "/" route, then use express.static for the rest?

I have a basic express app and I want to serve one file (after performing some logic) for the default route of /.
Unfortunately I can't use
app.use(function (res, res, next){
*logic here*
res.sendFile(filepath);
});
express.static()
because that will intercept every request and send the filepath for every request.
Is there another way of doing this?
It's enough to check the URI part of url and if it's / then send file.
Check this:
app.use(function (req, res, next) { // first must be Your middleware
if(req.path == '/') {
return res.sendFile('some file');
}
next();
});
app.use(express.static('public')); // and after it You can attach static middleware
or:
app.use(express.static('public'));
app.all('/', function (req, res) {
res.sendFile(filePath);
});
var regexp = /^\/\w+/
app.use(function (req, res, next){
if(!regexp.test(req.path)){
res.sendFile(filepath);
}
});
express.static()
this may work comment your requirement

Express basic authentication for serving static files

I'm using Express 4 framework and I need basic authentication for serving static files. This is what I have now:
app.use('/files', auth);
app.use('/files', express.static(path.join(__dirname, 'files')));
This works great if I try to access /files but if I wrote URL ../files/somefile.txt authentication is not needed and I'm able to access that file. I would want all the files under the "files"-directory to be accessible only by authenticated user.
It's an old thread but I just came across the same issue. I'm using http-auth package to restrict the access to a folder in my public directory.
The middleware was working fine when requesting the protected directory (get /protectedFolder shows the prompt for the authentication), but it skips the files when they're requested directly (get /protectedFolder/file.txt displays the content of file.txt)
I solved it by switching the order of middlewares, I initially had
app.use('/protected', express.static(path.join(__dirname, '../../../protected')));
app.use('/protected', auth.connect(basic), (req, res, next) => {
next();
});
But the correct order should be:
app.use('/protected', auth.connect(basic), (req, res, next) => {
next();
});
app.use('/protected', express.static(path.join(__dirname, '../../../protected')));
I hope this helps someone.
Have you tried the following:
app.use('/files/*', auth);
var basicAuth = require('basic-auth');
var auth = function(req, res, next){
var user = basicAuth(req);
if(user && user.name == "admin" && user.pass == "admin")
return next();
else{
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
return res.send(401);
}
}
app.use(function(req, res, next){
if(req.url.indexOf('ftp') != -1){
console.log(req.url);
return auth(req, res, next);
}
else
next();
});
app.use(express.static(path.join(__dirname, 'public')));
app.use('/ftp', serveIndex('public/ftp', {'icons': true, 'hidden': true, 'view': 'details'}))
Here is my code, it works fine for me, you can try it.
app.use('/files', auth , express.static(path.join(__dirname, 'files')));

What is the smartest way to handle robots.txt in Express?

I'm currently working on an application built with Express (Node.js) and I want to know what is the smartest way to handle different robots.txt for different environments (development, production).
This is what I have right now but I'm not convinced by the solution, I think it is dirty:
app.get '/robots.txt', (req, res) ->
res.set 'Content-Type', 'text/plain'
if app.settings.env == 'production'
res.send 'User-agent: *\nDisallow: /signin\nDisallow: /signup\nDisallow: /signout\nSitemap: /sitemap.xml'
else
res.send 'User-agent: *\nDisallow: /'
(NB: it is CoffeeScript)
There should be a better way. How would you do it?
Thank you.
Use a middleware function. This way the robots.txt will be handled before any session, cookieParser, etc:
app.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send("User-agent: *\nDisallow: /");
});
With express 4 app.get now gets handled in the order it appears so you can just use that:
app.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
1. Create robots.txt with following content :
User-agent: *
Disallow: # your rules here
2. Add it to public/ directory.
3. If not already present in your code, add:
app.use(express.static('public'))
Your robots.txt will be available to any crawler at http://yoursite.com/robots.txt
Looks like an ok way.
An alternative, if you'd like to be able to edit robots.txt as regular file, and possibly have other files you only want in production or development mode would be to use 2 separate directories, and activate one or the other at startup.
if (app.settings.env === 'production') {
app.use(express['static'](__dirname + '/production'));
} else {
app.use(express['static'](__dirname + '/development'));
}
then you add 2 directories with each version of robots.txt.
PROJECT DIR
development
robots.txt <-- dev version
production
robots.txt <-- more permissive prod version
And you can keep adding more files in either directory and keep your code simpler.
(sorry, this is javascript, not coffeescript)
Here is what I use
router.use('/robots.txt', function (req, res, next) {
res.type('text/plain')
res.send(
`User-agent: *
Disallow: /admin`);
});
For choosing the robots.txt depending the environment with a middleware way:
var env = process.env.NODE_ENV || 'development';
if (env === 'development' || env === 'qa') {
app.use(function (req, res, next) {
if ('/robots.txt' === req.url) {
res.type('text/plain');
res.send('User-agent: *\nDisallow: /');
} else {
next();
}
});
}
This is what I did on my index routes. You can just simply write down in your codes what I does given down below.
router.get('/', (req, res) =>
res.sendFile(__dirname + '/public/sitemap.xml')
)
router.get('/', (req, res) => {
res.sendFile(__dirname + '/public/robots.txt')
})
I use robots.txt as a normal file for Prod, and a middleware for other envs.
if(isDev || isStaging){
app.use('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nDisallow: /");
});
}
app.use(express.static(path.join(__dirname, 'public')));
Focusing more on the most convenient and simple solution instead of the "best" or "smartest". I simply added the following to the server.ts file.
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
What this does is create a robots.txt file on the fly and sends it whenever the /robots.txt file is called for.
Now to get this to work, the code fragment must be placed before the other server.get function calls (so it takes priority). I'm implementing Express with Angular, for which the full code fragment for me ended up being:
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/sophisticatedPrimate/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('/robots.txt', function (req, res) {
res.type('text/plain');
res.send("User-agent: *\nAllow: /");
})
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
app.use(express.static('public'))
app.use('/images', express.static('public/images'))
app.use('/videos', express.static('public/videos'))

Resources