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