Getting the entire file path in route - node.js

I am building a route to download files from our server. The problem is that the filepath contains forward slashes and then the filePath variable only contains the first folder.
NOTE: Don't worry - the code locks the filepath down to specific folders by checking the first folder to ensure the path begins inside a download directory.
I tried using different delimiters than the normal slash, like a +. The code does actually work if I do this but that's such a terrible hack. Is there any other way to do this?
In other words, this works:
http://localhost:5000/files/C/temp+uploads+upload_c64bc04e02
But this doesn't work:
http://localhost:5000/files/C/temp/uploads/upload_c64bc04e02
I really feel like I should be finding a way to get this to work.
router.get('/files/:bucketCode/:filePath', auth.check, async (req, res) => {
let bucketCode= req.params.bucketCode;
let filePath = req.params.filePath;

I'm posting this answer because finding out how to specify and reference wildcards in route definitions was more difficult than it needs to be.
This did it for me:
...
router.get('/files/*', auth.check, async (req, res) => {
let file_path = req.originalUrl.replace(req.baseUrl, '');
let file_name = file_path.substring((file_path.lastIndexOf('/') + 1));
...
It's worth noting this is not designed to work for all cases. I have limited use cases where, for example, I know I'm never requesting a file from root and that there will always be a file and at least one folder in the path.

Related

How can I stay organized when changing asset locations used by express.js server?

I have been learning express and everything seems to be clicking into place so I am trying now to concentrate on organization and keeping things clean and intuitive. I am wondering if there is a convention for storing where certain files are stored to be accessed by my server? The idea is for example I have a directory something like this:
root/
|--html
|--html1.html
but I want to change the structure mid project to:
root/
|--assets
|--html
|--html1.html
Or for example as a site scales I decide to move my public directory to S3 or some other cloud storage instead.
Now I have to go back everywhere that sends that file and change the path or the entire middleware to send an s3 file. My solution is this, and it works, but I am not sure if it is optimal or if there is a convention that might be better.
I can store everything in an asset module like this which has methods to return a file:
function getFile(path){
return fs.readFileSync(path)
}
module.exports = {
html1: getFile(`${__dirname}/html/html1.html`),
html2: getFile(`${__dirname}/html/html2.html`,
image1: getFile(`${__dirname}/image/image1.html`
}
and import it into my server as "assets" and use it like this:
app.get('/*', (req, res, next) => {
res.set('Content-Type', 'text/html');
res.send(assets.html1);
});
Now if I want to change the path or even use an asset from s3 I can simply change the logic/function/path etc all in one place rather than going through all my routers manually etc.
Any feedback or guidance on where to look for more info is greatly appreciated!

Is it possible in Node to read in a file an export its contents (without exporting asyncronly)?

TLDR: I want to read in a file's contents and then export a function which relies on those contents ... without making that exported function use promises or some other form of asynchronicity.
I'm trying to write an XML-validating module, and in order for it to do its thing I need to read in an XSD file. However, this only needs to happen once at "load time", so ideally I'd rather not have other modules that use my function have to wait for a promise to resolve to get their results. If I were using Webpack this would be easy, as I could use it's text file loader to bring in the XSD as if it were any other module ... but unfortunately I'm not.
In other words, currently I have to do (borderline pseudo-code):
module.exports.validate = () =>
new Promise((resolve) =>
fs.readFile(path, (file) => {
// use file to validate, then:
resolve(validationResult);
});
});
};
and instead I'd like to do:
fs.readFile(path, (file) => {
module.exports.validate = myValidationFunction;
});
But the above doesn't work because you can't export from callbacks, so my question is, is there any other way to accomplish this?
The https://github.com/jonschlinkert/to-exports library seems to offer exactly this, so it seems like it's possible ... but it doesn't work for me :(
P.S. At worst I could literally wrap the contents of the file inside the template string characters, rename the file to be .js, and export it that way:
module.exports = `*XSD contents go here*`;
However, that seems very kludgy, so I'm hoping there is a better way.
If you want to read a file synchronously, then use fs.readFileSync. It returns the contents of the file or throws an error.

Where should I put custom errors in sails.js?

I was wondering what's the best practice and if I should create:
a directory in which declare statically all the errors my application uses, like api/errors/custom1Error
declare them directly inside the files
or put the files directly inside the dir that needs that error, like api/controller/error/formInvalidError
other options!?
A neat way of going about this would be to simply add the errors as custom responses under api/responses. This way even the invocation becomes pretty neat. Although the doc says you should add them directly in the responses directory, I'm sure there must be a way to nest them under, say, responses/errors. I'll try that out and post an update in a bit.
Alright, off a quick search, I couldn't find any way to nest the responses, but you can use a small workaround that's not quite as neat:
Create the responses/errors directory with all the custom error response handlers. Create a custom response and name it something like custom.js. Then specify the response name while calling res.custom().
I'm adding a short snippet just for illustration:
api/responses/custom.js:
var customErrors = {
customError1: require('./errors/customError1'),
customError2: require('./errors/customError2')
};
module.exports = function custom (errorName, data) {
var req = this.req;
var res = this.res;
if (customErrors[errorName]) return customErrors[errorName](req, res, data);
else return res.negotiate();
}
From the controller:
res.custom('authError', data);
If you don't need logical processing for different errors, you can do away with the whole errors/ directory and directly invoke the respective views from custom.js:
module.exports = function custom (viewName, data) {
var req = this.req;
var res = this.res;
return res.view('errors/' + viewName, data);//assuming you have error views in views/errors
}
(You should first check if the view exists. Find out how on the linked page.)
Although I'm using something like this for certain purposes (dividing routes and so on), there definitely should be a way to include response handlers defined in different directories. (Perhaps by reconfiguring some grunt task?) I'll try to find that out and update if I find any success.
Good luck!
Update
Okay, so I found that the responses hook adds all files to res without checking if they are directories. So adding a directory under responses results in a TypeError from lodash. I may be reading this wrong but I guess it's reasonable to conclude that currently it's not possible to add a directory there, so I guess you'll have to stick to one of the above solutions.

Ignoring Files With Chokidar In Watched Folders

I'm trying to ignore specific files within folders using Chokidar. I'm sure the syntax for the ignore path is incorrect, but I can't seem to find the problem. I've tried all combinations of strings, globs, and arrays. I'd appreciate if someone would point me in the right direction.
Here's a quick example of the problem. I'm trying to ignore ignore.js, but since the folder is being watched, console.log is executed when both writing and deleting the file.
var chokidar = require('chokidar');
var fs = require('fs');
var path = require('path');
var watcher = chokidar.watch('./test', {
ignored: path.resolve('./test/ignore.js'),
persistent: true,
ignoreInitial: true,
alwaysState: true
});
watcher.on('all',console.log);
setTimeout(function(){fs.writeFile('./test/ignore.js', 'w');}, 200);
setTimeout(function(){fs.unlink('./test/ignore.js');}, 300);
Thanks for any help!
I am inclined to agree with #loganfsmyth's comment that your path name is wrong. In my app I dynamically lookup the folder chokidar is monitoring from a function. For instance
Meteor.methods({
getWatchFolder: function () {
return watchFolder;
},
});
I set watchFolder elsewhere, it's not really important in the context of this question, but assume it is returning "/tmp". This worked great, and ignored that file:
var watcher = chokidar.watch(Meteor.call('getWatchFolder'), {
ignored: path.resolve(Meteor.call('getWatchFolder')+'/ignore.js'),
persistent: true
});
I noticed this only ignored /tmp/ignore.js, not a nested instance like /tmp/tmp2/ignore.js. If you want to ignore all nested instances this is easily remedied by adding the double asterisk wildcard to the ignore path:
var watcher = chokidar.watch(Meteor.call('getWatchFolder'), {
ignored: path.resolve(Meteor.call('getWatchFolder')+'/**/ignore.js'),
persistent: true
});
I tried setting my watch folder to . like you. It found TONS of files, I determined it was running from
/Users/esoyke/myAppName/.meteor/local/build/programs/server
This was not respecting my ignore path though. When I changed it from watching . to that absolute path instead, it naturally found the same files but the ignore worked again. I suspect there is an issue with absolute vs. relative paths going on here, see if you can refactor to use an absolute path.
P.S. Thanks for showing me that path.resolve, I hadn't used that Node module yet. I was trying to add multiple sub-directories to the ignore and was trying to do so by editing chokidar's default regex of /[/\]./. This approach is much simpler and easier to read. Sadly it doesn't seem like chokidar's library allows multiple ignore values at the moment, and path.resolve returns an array when there are more than one arg to it, so I'll probably have to go back to regex to get multiple ignore paths working.

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