nodejs static content NOT using sendFile - node.js

How can I make nodejs serve up static content without having to use sendFile? The problem with sendFile is it opens another tab as you navigate from page to page, here is a snippet...
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/verify', function(request, response) {
var oneTimeCode = request.body.oneTimeCode;
var timeEntry = Date.now();
if (oneTimeCode == 'abc123') {
response.sendFile('/home/ubuntu/form.html');
} else {......
Can I use something like this in place of sendFile?
app.use(express.static(__dirname + '/home/ubuntu/form.html'));

express.static is not a good option to serve an individual file.
It is intended to serve all static assets inside a folder using the url for file name resolution as described here.
sendFile() shouldn't be responsible for opening new tabs. May be you have a wrong target on the anchor html element?
If you want to send static content without using sendFile() you can always open the static asset file and transfer the content over the response stream. This is what sendFile() does. You can review the source code and take ideas from there.

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.

pug.compile() cannot find template files (res.render() can)

I'm writing a small NodeJS/Express app. I set up pug as its template engine:
const app = express();
app.set('view engine', 'pug');
app.set('views', __dirname + "/public/views");
app.use(express.static(__dirname + '/public/static'));
This works fine when calling a res.render() to send HTML responses:
app.get('/', function getIndex(req, res){
res.render('index.pug');
});
But when I try to render small components and collect them in a string or an array as response to an AJAX call, I can't get it to work.
const pug = require('pug');
const compile = pug.compileFile('option.pug');
This always results in Error: ENOENT: no such file or directory, open 'option.pug'. I tried changing the path to the router's perspective (so something like ../../public/views/option.pug) but this also does not help.
I don't know why the paths are interpreted differently.
How do I refer to this template when using pug.compileFile?
From the Pug source code, the passed path is set as the filename in the options:
https://github.com/pugjs/pug/blob/926f7c720112cac76cfedb003e25e9f43d3a1767/packages/pug/lib/index.js#L354
This is then passed to handleTemplateCache to read the file:
https://github.com/pugjs/pug/blob/926f7c720112cac76cfedb003e25e9f43d3a1767/packages/pug/lib/index.js#L215
So ultimately the path is just being passed to fs.readFileSync, which treats relative paths as being relative to the current working directory, process.cwd().
You could generate the appropriate path using something like this:
const file = app.get('views') + '/option.pug';
It would be better to use path.join rather than string concatenation for building paths, https://nodejs.org/api/path.html#path_path_join_paths
const path = require('path');
const file = path.join(app.get('views'), 'option.pug');
If you don't want to (or can't) use app.get('views') you could just build up the absolute path by other means, such as using __dirname directly.
Note also that you can pass a callback to res.render which will be passed the rendered HTML instead of writing it to the response. That may allow you to avoid calling the template directly in the first place.

using parameters in url causing included files to be downloaded from same url

I want to get parameters from url and display content based on the parameters.
I am using express 4 here is my code
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/showcontent/:book/:page',
function(req, res) {
console.log("app.get parameters");
res.render('bookPage', { book: req.params.book,page: req.params.page });
}
);
in my public folder there are css and js files. when i use url with parameters like
mydomain.com/showcontent/book/page123
it tries to get css and js files from
mydomain.com/showcontent/book/page123/css/style.css
mydomain.com/showcontent/book/page123/js/script.js
while it should get the files from public folder from url
mydomain.com/css/style.css
mydomain.com/js/script.js
everything works fine if I use url format like this
/showcontent?book=1234&page=12345
but i dont want to use this format, I think some thing is missing in my code please someone help me solve to get parameters from url using forward slash format and still get css and js files from public directory

Getting the mime type from a request in nodeJS

Im learning nodejs but I ran into a roadblock. Im trying to setup a simple server that will serve static files. My problem is that unless I explicitly type in the extension in the url, I cannot get the file extension. The httpheader 'content-type' comes in as undefined .
Here is my code, pretty simple:
var http = require("http"),
path = require("path"),
fs = require("fs");
var server = http.createServer(function(request, response){
console.log([path.extname(request.url), request.headers['content-type']]);
var fileName = path.basename(request.url) || "index.html",
filePath = __dirname + '/public/' + fileName;
console.log(filePath);
fs.readFile( filePath, function(err,data){
if (err) throw err
response.end(data);
});
})
server.listen(3000)
Any ideas?
FYI I dont just wanna dive into connect or other, I wanna know whats going on before I drop the grind and go straight to modules.
So static web servers generally don't do any deep magic. For example, nginx has a small mapping of file extensions to mime types here: http://trac.nginx.org/nginx/browser/nginx/conf/mime.types
There's likely also some fallback logic defaulting to html. You can also use a database of file "magic numbers" as is used by the file utility to look at the beginning of the file data and guess based on that.
But there's no magic here. It's basically
go by the file extension when available
maybe go by the beginning of the file content as next option
use a default of html because normally only html resources have URLs with no extensions, whereas images, css, js, fonts, multimedia, etc almost always do use file extensions in their URIs
Also note that browsers generally have fairly robust set of checks that will intepret files correctly even when Content-Type headers are mismatched with the actual response body data.

Node.js module-specific static resources

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

Resources