X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=src%2Ffiles.js;h=876ff4e96798b938708cec8dfca372b8e39b56d5;hb=e628921a338684a4bc3f196c5c39beba8b8f9b68;hp=55e8978ac59908b78a5ffbac2ea2b40e7b76f69c;hpb=a7f450d7b80feb2c7125813aee56ee6519b33228;p=perso%2FImmae%2FProjets%2FNodejs%2FSurfer.git diff --git a/src/files.js b/src/files.js index 55e8978..876ff4e 100644 --- a/src/files.js +++ b/src/files.js @@ -1,91 +1,140 @@ '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) { var cbCalled = false; - var rd = fs.createReadStream(source); - rd.on("error", function(err) { - done(err); - }); + // ensure directory + mkdirp(path.dirname(target), function (error) { + if (error) return cb(error); - var wr = fs.createWriteStream(target); - wr.on("error", function(err) { - done(err); - }); + var rd = fs.createReadStream(source); + rd.on("error", function(err) { + done(err); + }); - wr.on("close", function(ex) { - done(); - }); + var wr = fs.createWriteStream(target); + wr.on("error", function(err) { + done(err); + }); + + wr.on("close", function(ex) { + done(); + }); - rd.pipe(wr); + rd.pipe(wr); - function done(err) { - if (!cbCalled) { - cb(err); - cbCalled = true; + function done(err) { + if (!cbCalled) { + cb(err); + cbCalled = true; + } } - } + }); } -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 && 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')); - if (!req.files.file) return next(new HttpError(400, 'missing file')); + 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, {})); @@ -96,17 +145,57 @@ function put(req, res, next) { }); } +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(403, 'Path not allowed')); + if (!absoluteFilePath) return next(new HttpError(404, 'Not found')); + + 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')); }); }); }