Node.js module-specific static resources - node.js

Is there an elegant way to bundle static client-side file resources (scripts,images,etc) into an Express module and systematically avoid naming conflicts? It's easy enough to register a module-specific instance of the static object like so:
app.use(express.static(modulePath + '/public'));
app.use(express.static(__dirname + '/public'));
but if both directories contain a "styles.css" file, it would seem that the one from the module will eclipse the one for the application. A subdirectory within the module's public could be used to avoid this problem, but what I really want is a way to map the module's resources to an arbitrary path such that
http://localhost:3000/mymodule/styles.css => <modulePath>/public/styles.css
http://localhost:3000/styles.css => <appPath>/public/styles.css
Is there already a way to do this? I've already done it with coding tricks, so I'm really looking for the recommended way to get it done. Also, if I'm missing some key concept that makes this totally unnecessary, I would like to learn about that too.

You can create another instance of connect.static and use it within a route:
app = express.createServer()
app.configure(function(){
app.use(express.static(__dirname+'/public'))
})
// new static middleware
var myModuleStatic = express.static(__dirname+'/mymodule')
// catch all sub-directory requests
app.get('/mymodule/*', function(req, res){
// remove subdir from url (static serves from root)
req.url = req.url.replace(/^\/mymodule/, '')
// call the actual middleware, catch pass-through (not found)
myModuleStatic(req, res, function(){
res.send(404)
})
})
app.listen(5555)

Something like this should also work:
app.use(express.static(__dirname + '/public'));
app.use('/mymodule', express.static(modulePath + '/public'));
See: http://groups.google.com/group/express-js/browse_thread/thread/c653fafc35fa85ed

Related

Apostrophe CMS - Protecting Static Content

I am working with apostrophe cms right now and have put the entire application behind a SAML IDP using Apostrophe-Saml. however, I have noticed that files that users upload, are placed into a 'public' directory and can be viewed without logging in. Is there a way that uploaded images/videos/files can be secured behind authentication?
The apostrophe-express module has a middleware option, but this does not appear to be used when accessing static content.
My next attempt was to override an apostrophe-assets method
self.servePublicAssets = function() {
var middleware = [];
if (self.lessMiddleware) {
// bc: only if the new implementation of enableLessMiddleware is in place.
// If it's an old override, it'll already be added to Express and
// this property won't be set
middleware.push(self.lessMiddleware);
}
//THIS NEXT LINE IS THE LINE OF INTEREST
middleware.push(self.apos.express.static(self.apos.rootDir + '/public'));
//SEE THE LINE ABOVE
self.expressMiddleware = {
// Run really early, before all of the stuff apostrophe-express normally
// puts in, for performance reasons. Preempts expensive
// queries related to `apostrophe-global` on every static file
when: 'beforeRequired',
middleware: middleware
};
};
I essentially tried to swap the 'line of interest' out for something that looks like this:
middleware.push(self.apos.app.use(self.apos.rootDir + '/public', authMethod(), self.apos.express.static(self.apos.rootDir + '/public')));
But this did not appear to work either. Does anyone have any ideas or know exactly how to do this? Thanks.
While difficult at the time the question was asked, this is now straightforward: install and configure the apostrophe-secure-attachments module.

Express and Handlebars: Implementing multiple themes

I and the team got a task to implement multiple themes for our project (keeping in mind that we have a single-theme project right now).
By theme I don't mean just CSS, I mean different markup files, certain features for certain things, etc. (If you don't like the word 'theme' in this context don't use it, I hope you get the idea: some things need to be reused across the codebase, some things need to be specific to a theme). Overall, we have no problem of structuring it in a maintainable way and implementing the most of it, but we don't know how to make multiple express-handlebars configs, which we can switch based on certain factors.
Right now we have a pretty standard express-handlebars project structure like such:
views/
index
layouts/
main.hbs
content.hbs
whatever.hbs
What we want is something like this:
views/
themes
theme1
layouts/
main.hbs
index.hbs
content.hbs
theme2
layouts/
main.hbs
index
We use a pretty standard way to initialize express-handlebars in our app startup file like this:
var handlebars = exphbs.create({
defaultLayout: 'main',
extname: '.hbs',
helpers: require('./server/hbsHelpers')
});
app.engine('.hbs', handlebars.engine);
app.set('view engine', '.hbs');
app.set('views', __dirname + '/views');
I was thinking to initialize several exphbs objects and switch them at runtime (request time) but this is not going to work since app.set would be an app-wide setting (long story short we host multiple websites with one app and can't afford change anything on app level, only on the express request level).
Desired behaviour is something like this:
res.render(‘/theme1/file.hbs’);
looks like there's no problem just to render directly without a configuration, but then we also need to specify that handlebars needs to grab layouts and partials for a specific theme from a specific source
or
res.render(‘file.hbs’, { theme: ‘theme1’ });
We're using this variant of express handlebars – https://github.com/ericf/express-handlebars
(May be you can advice an alternative that allows what I described above that we can interchange and not break a lot of things - our code base is pretty huge)?
Any of your help, ideas or advice would be highly appreciated.
You didn't mention how you would determine which theme each request would be rendered with but I think the simplest approach is to override the res.render() method.
app.use(function(req, res, next) {
// cache original render
var _render = res.render;
res.render = function(view, options, done) {
// custom logic to determine which theme to render
var theme = getThemeFromRequest(req);
// ends up rendering /themes/theme1/index.hbs
_render.call(this, 'themes/' + theme + '/' + view, options, done);
};
next();
});
function getThemeFromRequest(req) {
// in your case you probably would get this from req.hostname or something
// but this example will render the file from theme2 if you add ?theme=2 to the url
if(req.query && req.query.theme) {
return 'theme' + req.query.theme;
}
// default to theme1
return 'theme1';
}
The nice thing about this is your calls in your controller will still be clean - res.render('feature/index.hbs') as long as you have that file in each theme folder you should be good.
You could make getThemeFromRequest a lot smarter and it could check to see if the template exists for that theme and if not render the file from the default theme which may help prevent a bunch of duplicate html.

How can I match all requests except css/js/img in Express framework?

Messing around with Node.js and Express. I just have the following right now:
app.get('*', function(req, res) {
});
which should catch all urls. What's the best way to exclude static files -- that end in .css, .js and .png (or perhaps begin with "css/", "js/" and "img/") ?
Don't put your static files in ./views. Put them in ./public and add this line above app.use(app.router):
app.use(express.static(path.join(__dirname, 'public')));
That's all you need, you don't need anything else.

PassportJs's passport.session() conflicts with file uploader - won't write file

Okay, banging my head against the wall on this one. I just switched from everyauth to passportjs and everything worked out great, except my implementation of Valums file uploader.
You can see the Gist of my fileuploader code at https://gist.github.com/4066155
My app.configure for express looks like this:
app.configure( function () {
app.use(express.methodOverride());
app.use(express.static(__dirname + '/public'));
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view options', {
layout: false
});
app.use(passport.initialize());
app.use(passport.session());
});
Strangely enough, if I comment out app.use(passport.session()); - the file uploader seems to work fine. The passport.initialize can stay, as it is not causing any problems.
If I keep the use passport.session in my code, the upload call comes through, it actually does create a temporary file in my tmp directory, but it stays at zero bytes. The server never responds to the web client (no callback) and the file is never actually written by fs.
Even a pointer in the right direction might be very helpful. If you need more context let me know. Thanks.
Update:
If it helps, I diffed the req variable to see the difference when passport.session is not called vs when passport.session is called. The first diff is with passport.session NOT called and the second when it IS called. URL: http://diffchecker.com/Xk8g434Q - line 469 is interesting where it shows the events being bound, and then in the second block of text, events is just {} (empty).
The passport.session() method calls your passport.deserializeUser(), which itself usually makes a database call to fetch the user. This database call delays the execution of code that starts listening to the incoming data. That is, the data arrives while nobody is listening for it.
If anybody experiences this, one workaround, although not ideal, is to just add an extra layer of middleware around the PassportJS session middleware - it is obviously not going to authenticate the file uploads, but so far nobody has answered my question and I have still been unable to find why it conflicts.
Here's some code you can put in Express's app.configure( function () { just after the app.use(passport.initialize()).
var sessionMiddleware = passport.session()
app.use(function(req, res, next) {
if (req.url.match("/upload")) return next()
console.log("Using session", req.url)
sessionMiddleware(req, res, next)
})

Several individual static directories with different paths using Express

I would like to be able to get Express to treat several directories (not just one) as "static" -- that is, if the file is there, then serve it.
Connect's static() module seems to be geared up for people who want to make files in a specific directory available in the server's root. However, that's not what I want. What I am after, is end up with something like this:
GET /modules/MODULE1 -> Return files in modules/MODULE1/public
GET /modules/MODULE2 -> Return files in modules/MODULE2/public
GET /modules/MODULE3 -> Return files in modules/MODULE3/public
I am looking at the source of static, which in turns uses send, which in turns defines SendStream, which takes the file path straight from the request (which is not what I want).
Are there easy ways to do this?
Merc.
what's wrong with
app.use('/modules/MODULE1', express.static('modules/MODULE1/public'))
for each module?
The answer is here:
https://groups.google.com/forum/?fromgroups=#!topic/express-js/kK9muR0mjR4
Basically:
var st = express.static(
__dirname + app.set('path.static'),
{ maxAge : app.set('static.expiry') }
);
app.get(/^\/static\/(v.+?\/)?(.+$)/, function (req, res, next) {
req.url = req.params[1];
st(req, res, next);
});
Basically, since Static consider req.url, it's a matter of hacking it so that it "looks right" when/if the path matches.
I asked TJ if he would add it as an option, he (rightly) answered:
this isn't something send() should do, you can already do this easily with connect/express with several static() middleware, you would just have to do the same but more manual with send()
https://github.com/visionmedia/send/issues/10#issuecomment-8225096

Resources