'use strict'; var fs = require('fs'), path = require('path'), ejs = require('ejs'), rm = require('del'), debug = require('debug')('files'), mkdirp = require('mkdirp'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess; var gBasePath; exports = module.exports = function (basePath) { gBasePath = basePath; return { get: get, put: put, del: del }; }; // http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js function copyFile(source, target, cb) { var cbCalled = false; // ensure directory mkdirp(path.dirname(target), function (error) { if (error) return cb(error); var rd = fs.createReadStream(source); rd.on("error", function(err) { done(err); }); var wr = fs.createWriteStream(target); wr.on("error", function(err) { done(err); }); wr.on("close", function(ex) { done(); }); rd.pipe(wr); function done(err) { if (!cbCalled) { cb(err); cbCalled = true; } } }); } function render(view, options) { return ejs.render(fs.readFileSync(view, 'utf8'), options); } function getAbsolutePath(filePath) { var absoluteFilePath = path.resolve(gBasePath, filePath); if (absoluteFilePath.indexOf(gBasePath) !== 0) return null; return absoluteFilePath; } function get(req, res, next) { var filePath = req.params[0]; var absoluteFilePath = getAbsolutePath(filePath); if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed')); fs.stat(absoluteFilePath, function (error, result) { if (error) return next(new HttpError(404, error)); debug('get', absoluteFilePath); if (result.isFile()) return res.sendFile(absoluteFilePath); if (result.isDirectory()) return res.status(200).send({ entries: fs.readdirSync(absoluteFilePath) }); return next(new HttpError(500, 'unsupported type')); }); } function put(req, res, next) { var filePath = req.params[0]; if (!req.files.file) return next(new HttpError(400, 'missing file')); var absoluteFilePath = getAbsolutePath(filePath); if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed')); fs.stat(absoluteFilePath, function (error, result) { if (error && error.code !== 'ENOENT') return next(new HttpError(500, error)); debug('put', absoluteFilePath, req.files.file); if (result && result.isDirectory()) return next(new HttpError(409, 'cannot put on directories')); if (!result || result.isFile()) { return copyFile(req.files.file.path, absoluteFilePath, function (error) { if (error) return next(new HttpError(500, error)); next(new HttpSuccess(201, {})); }); } return next(new HttpError(500, 'unsupported type')); }); } function del(req, res, next) { var filePath = req.params[0]; var absoluteFilePath = getAbsolutePath(filePath); if (!absoluteFilePath) return next(new HttpError(404, 'Not found')); if (absoluteFilePath.slice(gBasePath.length) === '') return next(new HttpError(403, 'Forbidden')); fs.stat(absoluteFilePath, function (error, result) { if (error) return next(new HttpError(404, error)); rm(absoluteFilePath, function (error, result) { if (error) return next(new HttpError(500, 'Unable to remove')); next(new HttpSuccess(200, { entries: result })); }); }); }