Related
I try to add dynamically the routes but when I send a post request in with postman it return not found, and I can see in the app a 404 logged next to the endpoint.
SO I assume that the endpoint is not found but I know that I added the router dynamically, so why does express not see it? How can I make it work? Where am I doing wrong I can't see.
here the server.js:
app.set('port', process.env.PORT || 3000);
var server = http.createServer(app);
server.listen(port, onListening);
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
debug('Listening on ' + bind);
console.log(`\x1b[33mServer listening on port ${port}`);
}
Here where I app.js:
import celebrate from 'celebrate';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import dotenv from 'dotenv';
import express from 'express';
import createError from 'http-errors';
import logger from 'morgan';
import path from 'path';
import MountRouters from './utils/MountRouter';
dotenv.config();
const app = express();
app.use(logger('dev'));
app.use(cors());
app.use(express.json({ limit: '2GB' }));
app.use(express.urlencoded({ extended: false, limit: '50mb' }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'build')));
// create all the routers
(async () => {
await MountRouters(app);
})();
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
app.use(celebrate.errors());
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.send(err.message);
});
module.exports = app;
Here it is the MountRouters.js:
import { readdir } from 'fs/promises';
import path from 'path';
async function GetSubdirectories(dir) {
return (await readdir(dir, { withFileTypes: true })).filter(dirent => dirent.isDirectory());
}
async function MountRouters(app) {
const dirs = await GetSubdirectories(path.join(__dirname, '../routes'));
for (const dirent of dirs) {
const { name } = dirent;
const routerModule = path.join(name, 'router');
try {
const router = (await import(`../routes/${routerModule}`)).default;
if (router) {
console.log(`Mounting route: "/api/v1/${name}"`);
app.use(`/api/v1/${name}`, router);
}
} catch (err) {
console.log('ERROR mounting...', err);
console.info(`No route defined for module "${name}"`);
}
}
return app;
}
export default MountRouters;
Because your MountRouters() function is async, but you mount all the other routes synchronously, your dynamic routes actually get mounted last. This puts them after your 404 handler, which will then receive the requests before they do, and return a 404 response.
To address that, you can mount your other routes inside of the IIFE too:
...
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'build')));
// create all the routers
(async () => {
await MountRouters(app);
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
app.use(celebrate.errors());
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.send(err.message);
});
})();
module.exports = app;
The server is running and getting a lot of request while this route is not working all the other routes are working.
I am picking up error 404 with the following message.
my route works and after a while stops to work I don't know why and how this happens.
code example:
/images route
/**
* Root route, all validation will be done here.
* TODO:
*/
const { insertImgs,getExisting,getImg } = require('../core/DB.js');
const logger = require( '../logger/logger.js' ).getInstance();
class Images {
constructor(expressApp) {
if (!expressApp) throw 'no express app';
this.router = expressApp;
this.configure();
}
configure() {
this.router.get('/images',async (req, res, next) => {
try {
let images = await getImg(res.locals.config.siteConfig.ID, res.locals.config.siteConfig.ocrLicense)
/* merge results. */
return res.status(200).send( images );
} catch (err) {
/* log error */
console.log( 'alts failed', err );
logger.error({
type : 'req denied',
route : 'alts',
ip,
key : req.body.key,
data : req.body,
error : err
});
return res.status(503).send('Server Maintenance.');
}
});
return this.app;
}
}
module.exports = {
Images : Images
}
main.js
const express = require( "express" );
const cors = require( "cors" );
const http = require( 'http' );
const config = require( './config.js' );
const helmet = require("helmet");
/* routes */
const { Root } = require('./routes/root.js');
const { Alt } = require('./routes/alts.js');
const { Images } = require('./routes/images.js');
const { Health } = require('./routes/health.js');
const logger = require('./logger/logger.js').getInstance();
const app = express();
const server = http.createServer(app);
const port = config.port || 3003;
app.use( express.json() );
app.use( express.urlencoded( { extended: true } ) );
app.use( cors() );
app.use( helmet() );
/**
* init routes.
*/
[
Root,
Images,
Alt,
Health
].forEach(route => {
new route(app)
});
//404 last handler
app.use((req, res, next)=> {
res.status(404).send({error: 'Page not found'});
});
// error handler
app.use(function(err, req, res, next) {
// render the error page
res.status(err.status || 500);
res.send('error 404');
});
server.listen(port,()=>{
logger.log({ type : `server startup in process : ${ process.pid }` , port : port });
});
i would guess that it has to do with the fact that you are using the http module in conjunction with express.
i also wouldn't wrap each route in an object, express provides a Router class to do exactly that:
i rewrote some parts of your code to be a bit more concise:
main.js
const express = require("express")
const cors = require("cors")
const helmet = require("helmet")
const config = require('./config.js')
const logger = require('./logger/logger.js').getInstance()
const images_route = require('./routes/images.js')
const app = express()
const port = config.port || 3003
function page_not_found(req, res, next) {
return res.status(404).send({
error: 'Page not found'
})
}
function error_handler(err, req, res, next) {
res.status(err.status || 500)
return res.send('error 404')
}
app.use(
express.json(),
express.urlencoded({ extended: true }),
cors(),
helmet()
)
app.use('/images', image_route)
app.use(error_handler)
app.all('/', page_not_found)
app.listen(port, () => {
logger.log({
type: `server startup in process : ${ process.pid }`,
port: port
})
});
images.js
const express = require('express')
const route = express.Router()
const { insertImgs, getExisting, getImg } = require('../core/DB.js')
const logger = require('../logger/logger.js').getInstance()
route.get('/', async (req, res, next) => {
try {
let images = await getImg(
res.locals.config.siteConfig.ID,
res.locals.config.siteConfig.ocrLicense
)
return res
.status(200)
.send( images )
} catch (err) {
console.error('alts failed', err)
logger.error({
type: 'req denied',
route: 'alts',
ip,
key: req.body.key,
data: req.body,
error: err
})
return res
.status(503)
.send('Server Maintenance');
}
})
module.exports = route
it is much easier to see what routes are being used from your main.js file now, this makes it harder to have overlapping endpoints.
let me know if this helps at all.
try/catch does not work with async code. You also should not be using await inside a route handler as doing so may prevent other requests from getting processed.
Instead of this code:
this.router.get('/images',async (req, res, next) => {
try {
let images = await getImg(res.locals.config.siteConfig.ID, res.locals.config.siteConfig.ocrLicense)
/* merge results. */
return res.status(200).send( images );
} catch (err) {
/* log error */
console.log( 'alts failed', err );
logger.error({
type : 'req denied',
route : 'alts',
ip,
key : req.body.key,
data : req.body,
error : err
});
return res.status(503).send('Server Maintenance.');
}
});
Try this modified version:
this.router.get('/images', (req, res) => {
getImg(
res.locals.config.siteConfig.ID,
res.locals.config.siteConfig.ocrLicense
).then(images => {
res.status(200).send(images);
}).catch(err => {
console.log('alts failed', err);
logger.error({
type : 'req denied',
route : 'alts',
ip,
key : req.body.key,
data : req.body,
error : err
});
res.status(503).send('Server Maintenance.');
});
});
This assumes that getImg is either declared as an async function or returns a Promise.
I am building a boat visualizer using AISHub APIs. After inquiring the APIs I am able to obtain a json file with the vessels I am interested in and inject these vessels inside a table.
the problem I have is that after I receive and filter the data from the API, I would like to send them to MongoDB to store them. As of now MongoDB is not receiving any data and I don't know why?
According to the official documentation of MongoDB here is what I did to create the database:
After hitting connect to my application as shown below and copy/paste the key:
mongodb+srv://<username>:<password>#vessel-tracker-cluster-x2lpw.mongodb.net/test?retryWrites=true&w=majority
Below is how my cluser is organized:
And after accessing the collections you can see how the database is structured:
app.js
var app = express();
app.use(cors());
app.options('*', cors());
// DB Config
const db = require('./config/keys').MongoURI;
const options = {
useNewUrlParser: true,
reconnectTries: Number.MAX_VALUE,
poolSize: 10
};
mongoose
.connect(db, options)
.then(() => console.log('MongoDB Connection established'))
.catch((err) => console.log('Error connecting MongoDB database due to: ', err));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// Bodyparser
app.use(express.urlencoded({ extended: false }));
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
const PORT = process.env.PORT || 3000;
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(cors());
app.route('/vessels/all').get(vesselController.getBaseAll);
app.route('vessels/:id/track').get(vesselController.getCurrent);
app.route('/vessels').get(vesselController.getHistory);
app.listen(PORT, console.log(`Server started on port ${PORT}`));
module.exports = app;
index.js
var express = require('express');
var router = express.Router();
var axios = require('axios');
const NodeCache = require('node-cache');
const myCache = new NodeCache();
let hitCount = 0;
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
const mmsiOfInterest = [
'367029520',
'366909730',
'367128570'
];
const shipNamesOfInterest = [
'MICHIGAN',
'JP BOISSEAU',
'DELAWARE BAY
];
router.get('/hello', async function(req, res, next) {
const cData = myCache.get('cData');
if (!cData) {
hitCount++;
console.log(`hit ${hitCount} number of times`);
const { data } = await axios.get(
'http://data.aishub.net/ws.php?username=request'
);
const [ metaData, ships ] = data;
const shipsOfInterest = ships.filter(
(ship) => mmsiOfInterest.includes(ship.MMSI) || shipNamesOfInterest.includes(ship.NAME)
);
myCache.set('cData', shipsOfInterest, 70);
res.send(data);
return;
}
res.send(cData);
});
module.exports = router;
users.js
var express = require('express');
var router = express.Router();
// vessel models
const Vessles = require('../models/Vessels');
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.post('/vessles/map', function(req, res) {
const { callsign, name, imo, mmsi } = req.body;
let errors = [];
// Check required fields
if (!callsign || !name || !imo || !mmsi) {
errors.push({ msg: 'No data received' });
}
if (
Vessles.findOne({ mmsi: mmsi }).then((vessels) => {
if (vessels) {
// vessel exists
const newVessel = new Vessles({
callsign,
name,
imo,
mmsi
});
}
})
);
});
module.exports = router;
MondoDB Schema organization for Vessels.js
const mongoose = require('mongoose');
const VesselsSchema = new mongoose.Schema({
callsign: {
type: String,
required: true
},
name: {
type: String,
required: true
},
imo: {
type: Number,
required: false
},
mmsi: {
type: Number,
required: false
}
});
const Vessels = mongoose.model('Vessels', VesselsSchema);
module.exports = Vessels;
Posts that I have been reading to help me solve the problem but without success:
1) front end react is not sending data to db
2) connection to mongo db in react
3) This source was very useful but does not quite operate what I am trying to solve as it is more for rendering. Will surely be useful later though.
4) I thought that this source was extremely useful but didn't fully and clearly explain the process, otherwise with more explanation would probably be good.
Thanks for pointing in the right direction for solving this problem.
I have a node.js server combined with a react.js frontend. I've a form that requires users to upload files to a AWS S3 bucket. I'm using Multer.js to handle the file upload. I've set the default upload size to a max of 10mb. While there is absolutely no problem in uploading files as long as the file size below 10mb when testing using localhost. However, when I try to do the same from my Nginx Web Server, I get a '413 Error : Request entity too large' while trying to upload anything that is above 1mb. I've tried to follow the solutions here and here but to no luck.
I don't any error output except the 413 Error that my frontend app catches.
Here's the code to my express loader ( referencing this because I've tried the solutions mentioned above on these lines of code. )
const express = require("express");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const helmet = require("helmet");
const cors = require("cors");
const morgan = require("morgan");
const path = require("path");
const appRoot = require("app-root-path");
const loadRoutes = require("../api");
const logger = require("./logger");
const { userRoles } = require("../utils/consts");
const authService = require("../services/AuthService");
const expressLoader = (app) => {
app.use(cors());
app.use(express.static(path.join(appRoot.path, "public", "users")));
//app.use(express.static(path.join(appRoot.path, "public", "admin")));
// ATTACH IP ADDRESS TO EACH REQUEST
app.use((req, res, next) => {
req.ipAddress = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
return next();
});
// Extract token from header
app.use((req, res, next) => {
const token = req.headers["authorization"] ? req.header("authorization").split(" ")[1] : null;
if (token) {
req.token = token;
}
return next();
});
// Verify token
app.use(async (req, res, next) => {
if (req.token) {
const decode = await authService.verifyAuthToken(req.token);
console.log(decode);
if (!decode.tokenValid) {
logger.error(`[INVALID JWT ${req.path}] ip: ${req.ipAddress}`);
logger.error(decode.err);
req.isAuth = false;
return next();
} else {
req.isAuth = true;
req.decode = decode.data;
return next();
}
}
return next();
});
// Check if is admin
app.use((req, res, next) => {
const roleId = req.decode ? req.decode.role_id : null;
if (req.isAuth && (roleId === userRoles.SYSOP || roleId === userRoles.ADMIN)) {
req.isAdmin = true;
return next();
}
return next();
});
app.use(morgan("combined", { stream: logger.stream }));
app.use(helmet());
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
//app.use(cookieParser(process.env.SCOOK_SECRET));
// app.enable("trust proxy"); TO BE ENABLED FOR NGINX
// LOAD API
app.use(process.env.API_PREFIX, loadRoutes());
// app.use("/admin", (req, res, next) => {
// logger.info(`[ADMIN ROUTE ACCESSED FROM ${ req.ip }]`);
// return res.sendFile(path.join(appRoot.path + "/public/admin/index.html"));
// });
app.get("*", (req, res, next) => {
return res.sendFile(path.join(appRoot.path + "/public/users/index.html"));
});
}
module.exports = expressLoader;
Any help would be appreciated, thank you!
i have a Problem with my project. I want to make a little download system for pictures, so i made a router for /download/:filename. I have the pictures in /userdata/${userId}/ and if i request /download/ with a param like test it logs in my console, but if i use a param wich exists in the userdata folder like Download.jpg it redirects me back to the homepath of the user: /file/${userId} here is my code:
app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var index = require('./routes/index');
const admin = require("./routes/admin");
import file from "./routes/file";
import download from "./routes/download";
const session = require("express-session");
var app = express();
app.set("trust proxy", 1);
app.use(session({
secret: "bla",
resave: false,
cookie: {
maxAge: 120000000
},
saveUninitialized: false
}));
function checkIfLoggedin(req,res,next){
if(!(req.originalUrl === "/") && !req.session.loggedIn){
res.redirect('/');
return;
}
next();
};
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(checkIfLoggedin);
app.use('/', index);
app.use("/admin", admin);
app.use("/file", file);
app.use("/download", download);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
index.js router
var express = require('express');
var router = express.Router();
const bcrypt = require('bcrypt-nodejs');
var dbPassword;
import mysql from "mysql";
//
/* GET home page. */
router.get('/', function(req, res, next) {
if (req.session.user != undefined) {
res.redirect("/file/"+req.session.user.userId);
}
else{
res.render('index', {});
}
});
router.post('/', function(req,res,next) {
console.log("1");
const enteredUsername = req.body.username;
const enteredPassword = req.body.password;
const con = mysql.createConnection({
host: "localhost",
user: "user",
password: "pass",
database: "db"
});
con.query('SELECT * FROM users WHERE username = ?;', [`${enteredUsername}`], (error, results, fields) => {
if (results.length > 0) {
console.log("2");
console.log(error);
let dbPassword = results[0].password;
bcrypt.compare(enteredPassword, dbPassword, (err,response) => {
console.log(err);
console.log(response);
console.log("3");
if (response == true) {
req.session.user = {
userId: results[0].userId,
username: results[0].username,
isAdmin: results[0].isAdmin,
};
req.session.loggedIn = true;
console.log("file");
if (req.session.user.isAdmin) {
res.redirect("/admin");
}
else{
res.redirect("/file/" + req.session.user.userId);
}
}
else{
req.session.loggedIn = false;
console.log("false");
res.send("Falsches Passwort");
}
});
}
else{
res.send("Falsche Daten");
}
});
});
router.get("/logout", (req,res,next) => {
if (req.session.user.userId) {
req.session.destroy();
res.redirect("/");
}
});
module.exports = router;
file.js
import express from "express";
import fs from "fs";
const router = express.Router();
const userDataPath = "/srv/www/www.precode.tech/www/userdata/";
router.get("/:userId", (req,res,next) => {
//console.log(req.params.userId == req.session.user.userId);
if (req.params.userId == req.session.user.userId) {
const userDataFiles = fs.readdirSync(userDataPath+req.session.user.userId);
res.render("file", {files : userDataFiles, user: req.session.user});
}
else{
res.status(403).render("unauthorized");
}
//res.send(`${req.params.userId} ${req.session.user.userId}`);
});
/*router.get("/:userId/download/:filename", (req,res,next) => {
console.log(req.params.filename);
if (req.params.userId == req.session.user.userId) {
let filePath = `${__dirname}/../userdata/${req.session.user.userId}/`;
res.download(filePath, req.params.filename);
next();
};
});*/
export default router;
download.js
import express from "express";
const router = express.Router();
/*router.get("/", (req,res,next) => {
res.send("download");
});*/
router.get("/:filename", (req,res,next) =>{
console.log(req.params.filename);
});
export default router;
It would be very nice, if you have ideas or see the problem.
Thank you :)
EDIT: It should not redirect to the base path of the user, the get request on download should allways console.log the item
I did not find the flaw, but let's cleanup the code and fix middleware attaching sequence (at least I saw cookie parser attached after session middleware, I suspect only that part) and check.
But let's check my code review / cleanup.
Really hope it helps.
app.js:
const express = require('express');
const path = require('path');
//const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const session = require("express-session");
const mysql = require('mysql');
const db = mysql.createConnection({
host: "localhost",
user: "user",
password: "pass",
database: "db"
});
const app = express();
app.set("trust proxy", 1);
// set view engine and renderer
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// serve static files
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); // no need for it since express static will serve all static files in public folder
app.use(express.static(path.join(__dirname, 'public')));
// connect morgan to profile requests
app.use(logger('dev'));
// parse cookies first
app.use(cookieParser());
// then handle session
app.use(session({
secret: "bla",
resave: false,
cookie: {
maxAge: 120000000
},
saveUninitialized: true
}));
// handle requests data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use((req, res, next) => {
req.db = db; // attach db connection to request context
next();
});
// public routes that does not need auth check
app.use('/', require('./routes/index'));
const checkIfLoggedin = (req, res, next) => {
if (!req.session.loggedIn) {
return res.redirect('/');
}
res.locals.user = req.session.user;
next();
};
// internal routes that needs auth check
app.use(
'/admin',
checkIfLoggedin,
require('./routes/admin'));
app.use(
'/files',
checkIfLoggedin,
require('./routes/files'));
/* no need for this route, it's covered in files.js
app.use(
'/download',
checkIfLoggedin,
download);
*/
// catch 404 and forward to error handler
app.use((error, req, res, next) => {
if (error) return next(error);
const err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use((error, req, res, next) => {
// set locals, only providing error in development
res
.status(error.status || 500)
.render('error', {
message: error.message,
error: req.app.get('env') === 'development' ? error : {}
});
});
module.exports = app;
routes/index.js:
const express = require('express');
const router = express.Router();
const logger = require('winston');
const bcrypt = require('bcrypt-nodejs');
const _ = require('lodash'); // install it: npm i --save lodash
/* GET home page. */
router.get('/', (req, res) => {
if (_.get(req, 'session.user.userId')) {
return res.redirect("/files/" + req.session.user.userId);
}
res.render('index', {});
});
router.post('/auth', (req, res, next) => {
const {username, password} = req.body;
const db = req.db;
const query = 'SELECT * FROM users WHERE username = ? LIMIT 1';
const fields = [username];
db.query(
query,
fields,
(err, result) => {
if (err) {
logger.error(err);
const error = new Error('System fehler');
return next(error);
}
const user = _.get(result, '0');
if (!user) {
req.session.loggedIn = false;
const error = new Error('Benutzer nicht gefunden');
error.status = 403;
return next(error);
}
bcrypt.compare(password, user.password,
(err, isEqual) => {
if(err || !isEqual) {
if (err) logger.error('Error in password compare:', err);
const error = new Error('Passwort ungültig');
error.status = 403;
return next(error);
}
req.session.user = _.pick(user, ['id', 'userId', 'username', 'isAdmin']);
req.session.loggedIn = true;
if (user.isAdmin) {
return res.redirect("/admin");
}
res.redirect("/files/" + user.userId);
});
});
});
router.get("/logout", (req, res) => {
// simply destroy session and redirect,
// no need for session check
req.session.destroy();
res.redirect("/");
});
module.exports = router;
routes/files.js:
const express = require('express');
const router = express.Router();
const logger = require('winston');
const fs = require('fs');
const path = require('path');
const async = require('async');
const userDataPath = path.join(__dirname, '..', 'userdata');
// no need to check userId with session.user.userId
// since middleware attached in app.js will guard this route
// and redirect user to '/'
router.get('/:userId', (req, res, next) => {
if(req.params.userId != req.session.user.userId) {
const error = new Error("You cannot access other user's files");
error.status = 403;
return next(error);
}
const directory = path.join(userDataPath, req.params.userId);
logger.info('Reading directory:', directory);
fs.readdir(
directory,
(err, entries) => {
if (err) {
logger.error(err);
const error = new Error('System error');
return next(error);
}
const directories = [];
const files = [];
async.eachLimit(
entries, 10,
(entry, done) => {
fs.stat(path.join(dir, entry), (error, stat) => {
if (stat.isFile()) files.push(entry);
if (stat.isDirectory()) directories.push(entry);
done();
});
},
() => {
res.render("files", {
directories,
files
});
});
});
});
router.get('/:userId/download/:filename', (req, res, next) => {
if(req.params.userId != req.session.user.userId) {
const error = new Error("You cannot access other user's files");
error.status = 403;
return next(error);
}
res.download(path.join(userDataPath, req.params.userId, req.params.filename));
});
module.exports = router;
P.S. If it works behind nginx, apache and etc make sure userdata folder is not accessible publicly.