]> git.immae.eu Git - perso/Immae/Projets/Nodejs/Surfer.git/blob - src/files.js
Use the force
[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 del: del
21 };
22 };
23
24 // http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js
25 function copyFile(source, target, cb) {
26 var cbCalled = false;
27
28 // ensure directory
29 mkdirp(path.dirname(target), function (error) {
30 if (error) return cb(error);
31
32 var rd = fs.createReadStream(source);
33 rd.on("error", function(err) {
34 done(err);
35 });
36
37 var wr = fs.createWriteStream(target);
38 wr.on("error", function(err) {
39 done(err);
40 });
41
42 wr.on("close", function(ex) {
43 done();
44 });
45
46 rd.pipe(wr);
47
48 function done(err) {
49 if (!cbCalled) {
50 cb(err);
51 cbCalled = true;
52 }
53 }
54 });
55 }
56
57 function createDirectory(targetPath, callback) {
58 mkdirp(targetPath, function (error) {
59 if (error) return callback(error);
60 callback(null);
61 });
62 }
63
64 function getAbsolutePath(filePath) {
65 var absoluteFilePath = path.resolve(path.join(gBasePath, filePath));
66
67 if (absoluteFilePath.indexOf(gBasePath) !== 0) return null;
68 return absoluteFilePath;
69 }
70
71 function removeBasePath(filePath) {
72 return filePath.slice(gBasePath.length);
73 }
74
75 function get(req, res, next) {
76 var filePath = req.params[0];
77 var absoluteFilePath = getAbsolutePath(filePath);
78 if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed'));
79
80 fs.stat(absoluteFilePath, function (error, result) {
81 if (error) return next(new HttpError(404, error));
82
83 debug('get', absoluteFilePath);
84
85 if (!result.isDirectory() && !result.isFile()) return next(new HttpError(500, 'unsupported type'));
86 if (result.isFile()) return res.sendFile(absoluteFilePath);
87
88 async.map(fs.readdirSync(absoluteFilePath), function (filePath, callback) {
89 fs.stat(path.join(absoluteFilePath, filePath), function (error, result) {
90 if (error) return callback(error);
91
92 callback(null, {
93 isDirectory: result.isDirectory(),
94 isFile: result.isFile(),
95 atime: result.atime,
96 mtime: result.mtime,
97 ctime: result.ctime,
98 birthtime: result.birthtime,
99 size: result.size,
100 filePath: filePath
101 });
102 });
103 }, function (error, results) {
104 if (error) return next(new HttpError(500, error));
105 res.status(222).send({ entries: results });
106 });
107 });
108 }
109
110 function put(req, res, next) {
111 var filePath = req.params[0];
112
113 if (!(req.files && req.files.file) && !req.query.directory) return next(new HttpError(400, 'missing file or directory'));
114 if ((req.files && req.files.file) && req.query.directory) return next(new HttpError(400, 'either file or directory'));
115
116 var absoluteFilePath = getAbsolutePath(filePath);
117 if (!absoluteFilePath) return next(new HttpError(403, 'Path not allowed'));
118
119 fs.stat(absoluteFilePath, function (error, result) {
120 if (error && error.code !== 'ENOENT') return next(new HttpError(500, error));
121
122 debug('put', absoluteFilePath);
123
124 if (result && req.query.directory) return next(new HttpError(409, 'name already exists'));
125 if (result && result.isDirectory()) return next(new HttpError(409, 'cannot put on directories'));
126
127 if (req.query.directory) {
128 return createDirectory(absoluteFilePath, function (error) {
129 if (error) return next(new HttpError(500, error));
130 next(new HttpSuccess(201, {}));
131 });
132 } else if (!result || result.isFile()) {
133 return copyFile(req.files.file.path, absoluteFilePath, function (error) {
134 if (error) return next(new HttpError(500, error));
135 next(new HttpSuccess(201, {}));
136 });
137 }
138
139 return next(new HttpError(500, 'unsupported type'));
140 });
141 }
142
143 function del(req, res, next) {
144 var filePath = req.params[0];
145 var recursive = !!req.query.recursive;
146 var dryRun = !!req.query.dryRun;
147
148 var absoluteFilePath = getAbsolutePath(filePath);
149 if (!absoluteFilePath) return next(new HttpError(404, 'Not found'));
150
151 // absoltueFilePath has to have the base path prepended
152 if (absoluteFilePath.length <= gBasePath.length) return next(new HttpError(404, 'Not found'));
153
154 fs.stat(absoluteFilePath, function (error, result) {
155 if (error) return next(new HttpError(404, error));
156
157 if (result.isDirectory() && !recursive) return next(new HttpError(403, 'Is directory'));
158
159 // add globs to get file listing
160 if (result.isDirectory()) absoluteFilePath += '/**';
161
162 rm(absoluteFilePath, { dryRun: dryRun, force: true }).then(function (result) {
163 result = result.map(removeBasePath);
164 next(new HttpSuccess(200, { entries: result }));
165 }, function (error) {
166 console.error(error);
167 next(new HttpError(500, 'Unable to remove'));
168 });
169 });
170 }