Serve a file in a browser from a runtime directory using nodejs - node.js

In our application we store our reports in a user defined folders. User can add their own folders during runtime. Iam showing the history of those files in a web page. on clicking the file name i should show the file from the folder. How can i show the files from a non public directory.Since its given during runtime i havent added them as static dir to the express server.
One idea we tried was to use node-static-server and create a file server with the folder and serve the file. for each file we create this. it works fine but i get an error saying "port already in use". is there any better idea to do this? is this the right approach?

You can do this in NodeJS using a express.static:
const FS = require('fs')
const express = require('express')
const bp = require('body-parser')
const app = express()
function fileTest(req, res, next){
if (/\.|\/|\\/.test(req.params.file))
return res.sendStatus(400)
return next();
}
app.get(
'/static/:file',
fileTest,
function(req, res, next){
req.url = req.url.replace('/static','')
next()
},
express.static(
'./static',
{
fallthrough: false
}
)
)
app.post(
'/static/:file',
fileTest,
bp.text(),
function (req, res) {
FS.writeFile(
'./static/'+req.params.file,
req.body,
function (err) {
if(err)
return res.sendStatus(500)
return res.sendStatus(200)
}
)
}
)
app.listen(
1337
)
This is a simple example showing a server that will:
[POST]
Take a text body and load it into memory( pitfall: large bodies in memory )
Based on the URL, save it as a file in the static folder
[GET]
Search for a file
If found return file
The good news is that you can make the file and then request the file without restarting the server. Bad news is that this is a VERY SLOW server( comparatively to other options ).
As with all examples no good practices were followed, so be sure to adapt it to your needs.
Things to think about as you adopt it:
How do I allow people to save files to other folders?
How do I disallow people from saving files to other folders I don't want them to?
PROPER AUTHORIZATION

Related

How to process JS file returned from Express response.sendFile()

I have an API which uses Node.js + Express on the backend.
For one of the API endpoints, I'd like to use the Express response object method of "sendFile", documented here:
https://expressjs.com/en/api.html#res.sendFile
The API should return a Javascript file through the sendFile method.
What I can't figure out is how to read in the .js file on the front end so that I can use the JavaScript functions defined in the file. The sendFile portion appears to be working -- it's just the use of the file which I can't figure out.
Here's what I'm doing on the backend:
app.get("/api/member", async (req, res) => {
options = {
root: path.join(__dirname, '/static'),
dotfiles: 'deny'
}
res.sendFile("member.js", options, (err) => {
if (err) {
console.log(err)
next(err)
} else {
console.log('Sent file')
}
})
});
This seems to be working fine, as I can navigate to the endpoint on my localhost and it loads the JS file. The file member.js simply contains some javascript function definitions.
But, I can't figure out how to consume/use the file once it arrives to the front end.
Here's what I have currently on the frontend:
async function refreshJS() {
const url = `${baseUrl}/member`;
const response = await fetch(url, { credentials: "include" });
const script = document.createElement("script")
script.type = "text/javascript"
script.src = response.body
document.head.appendChild(script)
eval(script)
}
I've spent a lot of time looking through the console/debugger to find the text associated with the JS functions -- but they're nowhere to be found.
I've tested this general framework by loading JS files locally through the console and it worked, so I think it's wrapped up in a misunderstanding of where the JS functions live in the API response. For example, if I replace the command above of:
script.src = response.body
with
script.src = "member.js"
then everything works fine provided I have the file locally.
The examples that I've reviewed seem to deal exclusively with sending an HTML file which is loaded on the frontend. But, I can't find supporting documentation from the fetch API to understand how to use the JS file contents.

Best way to handle dynamic routes with its own logic in ExpressJS?

I've been tasked with something at work that's beyond my current skills so any help is appreciated.
I'm building an admin where you can add "games". Each game needs to have it's own front-end, routes, and logic.
Kinda like,
mainsite.com/game/game1
mainsite.com/game/game2
mainsite.com/game/game3
At the moment I'm just creating a directory based on the game name.
var dir = "./games/" + req.body.gameId;
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
In turn I can pull the .ejs file via:
/* GET dynamic game page. */
router.get("/game/:game", function(req, res, next) {
res.render("../games/"+req.params.game+"/index", { title: "Express" });
});
But I am confused on how it can have it's own logic, routes, connecting to database, front-end, stylesheets inside it's own folder.
There must be a better way to achieve this right?
Cheers
Yes! In Express, you can call app.use() inside a route. You will be able to define a public folder to contain the CSS, JS, and assets that are specific to each route. Just call app.use(express.static('route/to/assets')) inside the route.
app.get('/game/:game', (req, res) => {
app.use(express.static(req.params.game + '/public'))
res.render('../games/' + req.params.game + "/index", { title: "Express" })
})
Seems strange, but perfectly allowed.

sending multiple files down the pipe

we're using express 4 and right now I have something like this:
var express = require('express'),
router = express.Router();
router.get('/local_modules/*', function (req, res, next) {
var moduleName = req.url.match(/local_modules\/(.*?)\//).pop(1)
res.sendFile(filePath + '.js');
}
and I wanna do something more like:
router.get('/local_modules/*', function (req, res, next) {
var moduleDir = req.url.match(/local_modules\/(.*?)\//).pop(1)
fs.readdir(moduleDir, function(err, files) {
files.forEach(function(f) {
res.sendFile(path.join(moduleDir, f));
})
}
}
But that doesn't work. How can I serve multiple files with express? Note: not just all files in a directory (like in the example) - which probably can be done with app.use; express.static , but specific set of files (e.g. I may need to get the list of files from bower.json)
There is no way to send multiple files like that in a single response, unless you use your own special formatting (standard multipart or otherwise) and then parse that on the client side (e.g. via XHR).
Probably the easiest workaround would be to archive (zip, 7zip, tarball, etc.) the files and then serve that archive instead. This assumes however that you want the user to download it and not use the files in the browser (unless you have a zip, etc. parser in the browser and use XHR).

Node Express auth status

I have multiple routes, split into different files (my app consists of different "modules", which I maintain in separate folders. For each folder, there is an index.js file in which I manage the routes per module, and I require these in the app.js file).
For every route, I will require to check the auth, and pass the loggedIn status to the header of every page:
//Default variables for the ejs template
var options = {
loggedIn: true
};
res.render("home/home", options);
If the logged in status is true, then the user's name will be displayed. If not, the login / signup labels are displayed.
What is the best way to centralise this, so that I don't need to require the auth script in every of these index.js (route) files?
I need to be able to pass the auth status to the view via the options object (see example).
In your auth, module, use a middleware function. That function can check and store res.locals.loggedIn which will be available for any view that will eventually be rendered. Just make sure the app.use call executes prior to your other routes and it will work properly.
app.use(function auth(req, res, next) {
res.locals.loggedIn = true; // compute proper value here
next();
});
From what I understand you need to do this for every request.One common thing is adding this as middleware so that all the request gets this .
For Example :
var http = require('http');
var connect = require('connect');
var app = connect();
app.use(function(req, res) {
res.end('Hello!');
});
http.createServer(app).listen(3000)
Now for every request , Hello is printed . You could extract this as a module and reuse it across projects. Check here for more details

NodeJS exports not defined in external JS file

I am calling a function from an external JS file in my routes file. So, I am trying to export a function from my externalFile.js file. However, when I run the node server, it throws Uncaught ReferenceError: exports is not defined . externalFile.js is in public/javascripts/ and the route files is in routes/.
External file (externalFile.js):
exports.capsolvingComplete = function (stdout){
//Received, display text and hide the spinner, put check in place.
$('#capsolv-complete').css("display", "block")
$('#capsolv-output').text(stdout);
}
Route file:
var express = require('express');
var router = express.Router();
var script = require('../public/javascripts/externalFile.js')
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { title: 'CapSolv' });
});
router.post('/file-upload', function(req, res){
script.capsolvingComplete("HI");
res.end("success");
})
module.exports = router;
Thank you for your help! Bit of a node noob here.
Looking at your example, mscdex is right. Anything you require in Node is designed to run on the server, and doesn't have access to the browser's DOM.
I would recommend building a REST API that accepts the file upload, stores it, and returns a JSON object containing metadata: i.e. where the file was stored, size, etc., and let your client deal with that.

Resources