]> git.immae.eu Git - perso/Immae/Projets/Nodejs/Surfer.git/blob - src/files.js
Fix file deletion and add dry-run option
[perso/Immae/Projets/Nodejs/Surfer.git] / src / files.js
1 'use strict';
2
3 var fs = require('fs'),
4 path = require('path'),
5 rm = require('del'),
6 debug = require('debug')('files'),
7 mkdirp = require('mkdirp'),
8 HttpError = require('connect-lastmile').HttpError,
9 HttpSuccess = require('connect-lastmile').HttpSuccess;
10
11 var gBasePath;
12
13 exports = module.exports = function (basePath) {
14 gBasePath = basePath;
15
16 return {
17 get: get,
18 put: put,
19 del: del
20 };
21 };
22
23 // http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js
24 function copyFile(source, target, cb) {
25 var cbCalled = false;
26
27 // ensure directory
28 mkdirp(path.dirname(target), function (error) {
29 if (error) return cb(error);
30
31 var rd = fs.createReadStream(source);
32 rd.on("error", function(err) {
33 done(err);
34 });
35
36 var wr = fs.createWriteStream(target);
37 wr.on("error", function(err) {
38 done(err);
39 });
40
41 wr.on("close", function(ex) {
42 done();
43 });
44
45 rd.pipe(wr);
46
47 function done(err) {
48 if (!cbCalled) {
49 cb(err);
50 cbCalled = true;
51 }
52 }
53 });
54 }
55
56 function getAbsolutePath(filePath) {
57 var absoluteFilePath = path.resolve(path.join(gBasePath, filePath));
58
59 if (absoluteFilePath.indexOf(gBasePath) !== 0) return null;
60 return absoluteFilePath;
61 }
62
63 function removeBasePath(filePath) {
64 return filePath.slice(gBasePath.length);
65 }
66
67 function get(req, res, next) {
68 var filePath = req.params[0];
69 var absoluteFilePath = getAbsolutePath(filePath);
70 if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed'));
71
72 fs.stat(absoluteFilePath, function (error, result) {
73 if (error) return next(new HttpError(404, error));
74
75 debug('get', absoluteFilePath);
76
77 if (result.isFile()) return res.sendFile(absoluteFilePath);
78 if (result.isDirectory()) return res.status(222).send({ entries: fs.readdirSync(absoluteFilePath) });
79
80 return next(new HttpError(500, 'unsupported type'));
81 });
82 }
83
84 function put(req, res, next) {
85 var filePath = req.params[0];
86
87 if (!req.files.file) return next(new HttpError(400, 'missing file'));
88
89 var absoluteFilePath = getAbsolutePath(filePath);
90 if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed'));
91
92 fs.stat(absoluteFilePath, function (error, result) {
93 if (error && error.code !== 'ENOENT') return next(new HttpError(500, error));
94
95 debug('put', absoluteFilePath, req.files.file);
96
97 if (result && result.isDirectory()) return next(new HttpError(409, 'cannot put on directories'));
98 if (!result || result.isFile()) {
99 return copyFile(req.files.file.path, absoluteFilePath, function (error) {
100 if (error) return next(new HttpError(500, error));
101 next(new HttpSuccess(201, {}));
102 });
103 }
104
105 return next(new HttpError(500, 'unsupported type'));
106 });
107 }
108
109 function del(req, res, next) {
110 var filePath = req.params[0];
111 var recursive = !!req.query.recursive;
112 var dryRun = !!req.query.dryRun;
113
114 var absoluteFilePath = getAbsolutePath(filePath);
115 if (!absoluteFilePath) return next(new HttpError(404, 'Not found'));
116
117 // absoltueFilePath has to have the base path prepended
118 if (absoluteFilePath.length <= gBasePath.length) return next(new HttpError(404, 'Not found'));
119
120 fs.stat(absoluteFilePath, function (error, result) {
121 if (error) return next(new HttpError(404, error));
122
123 if (result.isDirectory() && !recursive) return next(new HttpError(403, 'Is directory'));
124
125 // add globs to get file listing
126 if (result.isDirectory()) absoluteFilePath += '/**';
127
128 rm(absoluteFilePath, { dryRun: dryRun }).then(function (result) {
129 result = result.map(removeBasePath);
130 next(new HttpSuccess(200, { entries: result }));
131 }, function (error) {
132 console.error(error);
133 next(new HttpError(500, 'Unable to remove'));
134 });
135 });
136 }