]> git.immae.eu Git - perso/Immae/Projets/Nodejs/Surfer.git/blob - src/files.js
Make listen port and ldap filter more flexible
[perso/Immae/Projets/Nodejs/Surfer.git] / src / files.js
1 'use strict';
2
3 var async = require('async'),
4 fs = require('fs'),
5 path = require('path'),
6 rm = require('del'),
7 debug = require('debug')('files'),
8 mkdirp = require('mkdirp'),
9 HttpError = require('connect-lastmile').HttpError,
10 HttpSuccess = require('connect-lastmile').HttpSuccess;
11
12 var gBasePath;
13
14 exports = module.exports = function (basePath) {
15 gBasePath = basePath;
16
17 return {
18 get: get,
19 put: put,
20 post: post,
21 del: del
22 };
23 };
24
25 // http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js
26 function copyFile(source, target, cb) {
27 var cbCalled = false;
28
29 // ensure directory
30 mkdirp(path.dirname(target), function (error) {
31 if (error) return cb(error);
32
33 var rd = fs.createReadStream(source);
34 rd.on("error", function(err) {
35 done(err);
36 });
37
38 var wr = fs.createWriteStream(target);
39 wr.on("error", function(err) {
40 done(err);
41 });
42
43 wr.on("close", function(ex) {
44 done();
45 });
46
47 rd.pipe(wr);
48
49 function done(err) {
50 if (!cbCalled) {
51 cb(err);
52 cbCalled = true;
53 }
54 }
55 });
56 }
57
58 function createDirectory(targetPath, callback) {
59 mkdirp(targetPath, function (error) {
60 if (error) return callback(error);
61 callback(null);
62 });
63 }
64
65 function isProtected(targetPath) {
66 return targetPath.indexOf(getAbsolutePath('_admin')) === 0;
67 }
68
69 function getAbsolutePath(filePath) {
70 var absoluteFilePath = path.resolve(path.join(gBasePath, filePath));
71
72 if (absoluteFilePath.indexOf(gBasePath) !== 0) return null;
73 return absoluteFilePath;
74 }
75
76 function removeBasePath(filePath) {
77 return filePath.slice(gBasePath.length);
78 }
79
80 function get(req, res, next) {
81 var filePath = decodeURIComponent(req.params[0]);
82 var absoluteFilePath = getAbsolutePath(filePath);
83 if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed'));
84
85 fs.stat(absoluteFilePath, function (error, result) {
86 if (error) return next(new HttpError(404, error));
87
88 debug('get', absoluteFilePath);
89
90 if (!result.isDirectory() && !result.isFile()) return next(new HttpError(500, 'unsupported type'));
91 if (result.isFile()) return res.download(absoluteFilePath);
92
93 async.map(fs.readdirSync(absoluteFilePath), function (filePath, callback) {
94 fs.stat(path.join(absoluteFilePath, filePath), function (error, result) {
95 if (error) return callback(error);
96
97 callback(null, {
98 isDirectory: result.isDirectory(),
99 isFile: result.isFile(),
100 atime: result.atime,
101 mtime: result.mtime,
102 ctime: result.ctime,
103 birthtime: result.birthtime,
104 size: result.size,
105 filePath: filePath
106 });
107 });
108 }, function (error, results) {
109 if (error) return next(new HttpError(500, error));
110 res.status(222).send({ entries: results });
111 });
112 });
113 }
114
115 function post(req, res, next) {
116 var filePath = decodeURIComponent(req.params[0]);
117
118 if (!(req.files && req.files.file) && !req.query.directory) return next(new HttpError(400, 'missing file or directory'));
119 if ((req.files && req.files.file) && req.query.directory) return next(new HttpError(400, 'either file or directory'));
120
121 debug('post:', filePath);
122
123 var absoluteFilePath = getAbsolutePath(filePath);
124 if (!absoluteFilePath || isProtected(absoluteFilePath)) return next(new HttpError(403, 'Path not allowed'));
125
126 fs.stat(absoluteFilePath, function (error, result) {
127 if (error && error.code !== 'ENOENT') return next(new HttpError(500, error));
128
129 if (result && req.query.directory) return next(new HttpError(409, 'name already exists'));
130 if (result && result.isDirectory()) return next(new HttpError(409, 'cannot post on directories'));
131
132 if (req.query.directory) {
133 return createDirectory(absoluteFilePath, function (error) {
134 if (error) return next(new HttpError(500, error));
135 next(new HttpSuccess(201, {}));
136 });
137 } else if (!result || result.isFile()) {
138 return copyFile(req.files.file.path, absoluteFilePath, function (error) {
139 if (error) return next(new HttpError(500, error));
140 next(new HttpSuccess(201, {}));
141 });
142 }
143
144 return next(new HttpError(500, 'unsupported type'));
145 });
146 }
147
148 function put(req, res, next) {
149 var oldFilePath = decodeURIComponent(req.params[0]);
150
151 if (!req.body || !req.body.newFilePath) return next(new HttpError(400, 'missing newFilePath'));
152
153 var newFilePath = decodeURIComponent(req.body.newFilePath);
154
155 debug('put: %s -> %s', oldFilePath, newFilePath);
156
157 var absoluteOldFilePath = getAbsolutePath(oldFilePath);
158 if (!absoluteOldFilePath || isProtected(absoluteOldFilePath)) return next(new HttpError(403, 'Path not allowed'));
159
160 var absoluteNewFilePath = getAbsolutePath(newFilePath);
161 if (!absoluteNewFilePath || isProtected(absoluteNewFilePath)) return next(new HttpError(403, 'Path not allowed'));
162
163 fs.rename(absoluteOldFilePath, absoluteNewFilePath, function (error) {
164 if (error) return next (new HttpError(500, error));
165
166 debug('put: successful');
167
168 return next(new HttpSuccess(200, {}));
169 });
170 }
171
172 function del(req, res, next) {
173 var filePath = decodeURIComponent(req.params[0]);
174 var recursive = !!req.query.recursive;
175 var dryRun = !!req.query.dryRun;
176
177 var absoluteFilePath = getAbsolutePath(filePath);
178 if (!absoluteFilePath) return next(new HttpError(404, 'Not found'));
179
180 if (isProtected(absoluteFilePath)) return next(new HttpError(403, 'Path not allowed'));
181
182 // absoltueFilePath has to have the base path prepended
183 if (absoluteFilePath.length <= gBasePath.length) return next(new HttpError(404, 'Not found'));
184
185 fs.stat(absoluteFilePath, function (error, result) {
186 if (error) return next(new HttpError(404, error));
187
188 if (result.isDirectory() && !recursive) return next(new HttpError(403, 'Is directory'));
189
190 // add globs to get file listing
191 if (result.isDirectory()) absoluteFilePath += '/**';
192
193 rm(absoluteFilePath, { dryRun: dryRun, force: true }).then(function (result) {
194 result = result.map(removeBasePath);
195 next(new HttpSuccess(200, { entries: result }));
196 }, function (error) {
197 console.error(error);
198 next(new HttpError(500, 'Unable to remove'));
199 });
200 });
201 }