'use strict';
-var fs = require('fs'),
+var async = require('async'),
+ fs = require('fs'),
path = require('path'),
- ejs = require('ejs'),
- rimraf = require('rimraf'),
+ rm = require('del'),
+ debug = require('debug')('files'),
mkdirp = require('mkdirp'),
HttpError = require('connect-lastmile').HttpError,
HttpSuccess = require('connect-lastmile').HttpSuccess;
-exports = module.exports = {
- get: get,
- put: put,
- del: del
-};
+var gBasePath;
+
+exports = module.exports = function (basePath) {
+ gBasePath = basePath;
-var FILE_BASE = path.resolve(__dirname, '../files');
+ return {
+ get: get,
+ put: put,
+ post: post,
+ del: del
+ };
+};
// http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js
function copyFile(source, target, cb) {
});
}
-function render(view, options) {
- return ejs.render(fs.readFileSync(view, 'utf8'), options);
+function createDirectory(targetPath, callback) {
+ mkdirp(targetPath, function (error) {
+ if (error) return callback(error);
+ callback(null);
+ });
+}
+
+function isProtected(targetPath) {
+ return targetPath.indexOf(getAbsolutePath('_admin')) === 0;
}
function getAbsolutePath(filePath) {
- var absoluteFilePath = path.resolve(FILE_BASE, filePath);
+ var absoluteFilePath = path.resolve(path.join(gBasePath, filePath));
- if (absoluteFilePath.indexOf(FILE_BASE) !== 0) return null;
+ if (absoluteFilePath.indexOf(gBasePath) !== 0) return null;
return absoluteFilePath;
}
+function removeBasePath(filePath) {
+ return filePath.slice(gBasePath.length);
+}
+
function get(req, res, next) {
- var filePath = req.params[0];
+ var filePath = decodeURIComponent(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));
- console.log('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'));
+ debug('get', absoluteFilePath);
+
+ if (!result.isDirectory() && !result.isFile()) return next(new HttpError(500, 'unsupported type'));
+ if (result.isFile()) return res.sendFile(absoluteFilePath);
+
+ async.map(fs.readdirSync(absoluteFilePath), function (filePath, callback) {
+ fs.stat(path.join(absoluteFilePath, filePath), function (error, result) {
+ if (error) return callback(error);
+
+ callback(null, {
+ isDirectory: result.isDirectory(),
+ isFile: result.isFile(),
+ atime: result.atime,
+ mtime: result.mtime,
+ ctime: result.ctime,
+ birthtime: result.birthtime,
+ size: result.size,
+ filePath: filePath
+ });
+ });
+ }, function (error, results) {
+ if (error) return next(new HttpError(500, error));
+ res.status(222).send({ entries: results });
+ });
});
}
-function put(req, res, next) {
- var filePath = req.params[0];
+function post(req, res, next) {
+ var filePath = decodeURIComponent(req.params[0]);
- if (!req.files.file) return next(new HttpError(400, 'missing file'));
+ if (!(req.files && req.files.file) && !req.query.directory) return next(new HttpError(400, 'missing file or directory'));
+ if ((req.files && req.files.file) && req.query.directory) return next(new HttpError(400, 'either file or directory'));
+
+ debug('post:', filePath);
var absoluteFilePath = getAbsolutePath(filePath);
- if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed'));
+ if (!absoluteFilePath || isProtected(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));
- console.log('put', absoluteFilePath, req.files.file);
+ if (result && req.query.directory) return next(new HttpError(409, 'name already exists'));
+ if (result && result.isDirectory()) return next(new HttpError(409, 'cannot post on directories'));
- if (result && result.isDirectory()) return next(new HttpError(409, 'cannot put on directories'));
- if (!result || result.isFile()) {
+ if (req.query.directory) {
+ return createDirectory(absoluteFilePath, function (error) {
+ if (error) return next(new HttpError(500, error));
+ next(new HttpSuccess(201, {}));
+ });
+ } else 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, {}));
});
}
+function put(req, res, next) {
+ var oldFilePath = decodeURIComponent(req.params[0]);
+
+ if (!req.body || !req.body.newFilePath) return next(new HttpError(400, 'missing newFilePath'));
+
+ var newFilePath = decodeURIComponent(req.body.newFilePath);
+
+ debug('put: %s -> %s', oldFilePath, newFilePath);
+
+ var absoluteOldFilePath = getAbsolutePath(oldFilePath);
+ if (!absoluteOldFilePath || isProtected(absoluteOldFilePath)) return next(new HttpError(403, 'Path not allowed'));
+
+ var absoluteNewFilePath = getAbsolutePath(newFilePath);
+ if (!absoluteNewFilePath || isProtected(absoluteNewFilePath)) return next(new HttpError(403, 'Path not allowed'));
+
+ fs.rename(absoluteOldFilePath, absoluteNewFilePath, function (error) {
+ if (error) return next (new HttpError(500, error));
+
+ debug('put: successful');
+
+ return next(new HttpSuccess(200, {}));
+ });
+}
+
function del(req, res, next) {
- var filePath = req.params[0];
+ var filePath = decodeURIComponent(req.params[0]);
+ var recursive = !!req.query.recursive;
+ var dryRun = !!req.query.dryRun;
+
var absoluteFilePath = getAbsolutePath(filePath);
if (!absoluteFilePath) return next(new HttpError(404, 'Not found'));
- if (absoluteFilePath.slice(FILE_BASE.length) === '') return next(new HttpError(403, 'Forbidden'));
+
+ if (isProtected(absoluteFilePath)) return next(new HttpError(403, 'Path not allowed'));
+
+ // absoltueFilePath has to have the base path prepended
+ if (absoluteFilePath.length <= gBasePath.length) return next(new HttpError(404, 'Not found'));
fs.stat(absoluteFilePath, function (error, result) {
if (error) return next(new HttpError(404, error));
- rimraf(absoluteFilePath, function (error) {
- if (error) return next(new HttpError(500, 'Unable to remove'));
- next(new HttpSuccess(200, {}));
+ if (result.isDirectory() && !recursive) return next(new HttpError(403, 'Is directory'));
+
+ // add globs to get file listing
+ if (result.isDirectory()) absoluteFilePath += '/**';
+
+ rm(absoluteFilePath, { dryRun: dryRun, force: true }).then(function (result) {
+ result = result.map(removeBasePath);
+ next(new HttpSuccess(200, { entries: result }));
+ }, function (error) {
+ console.error(error);
+ next(new HttpError(500, 'Unable to remove'));
});
});
}