aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/files.js
blob: 3b28d3ff31d6e3fd6378338001df8c9bd34f9dd9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
'use strict';

var fs = require('fs'),
    path = require('path'),
    rm = require('del'),
    debug = require('debug')('files'),
    mkdirp = require('mkdirp'),
    HttpError = require('connect-lastmile').HttpError,
    HttpSuccess = require('connect-lastmile').HttpSuccess;

var gBasePath;

exports = module.exports = function (basePath) {
    gBasePath = basePath;

    return {
        get: get,
        put: put,
        del: del
    };
};

// http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js
function copyFile(source, target, cb) {
    var cbCalled = false;

    // ensure directory
    mkdirp(path.dirname(target), function (error) {
        if (error) return cb(error);

        var rd = fs.createReadStream(source);
            rd.on("error", function(err) {
            done(err);
        });

        var wr = fs.createWriteStream(target);
            wr.on("error", function(err) {
            done(err);
        });

        wr.on("close", function(ex) {
            done();
        });

        rd.pipe(wr);

        function done(err) {
            if (!cbCalled) {
                cb(err);
                cbCalled = true;
            }
        }
    });
}

function getAbsolutePath(filePath) {
    var absoluteFilePath = path.resolve(gBasePath, filePath);

    if (absoluteFilePath.indexOf(gBasePath) !== 0) return null;
    return absoluteFilePath;
}

function get(req, res, next) {
    var filePath = 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));

        debug('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'));
    });
}

function put(req, res, next) {
    var filePath = req.params[0];

    if (!req.files.file) return next(new HttpError(400, 'missing file'));

    var absoluteFilePath = getAbsolutePath(filePath);
    if (!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));

        debug('put', absoluteFilePath, req.files.file);

        if (result && result.isDirectory()) return next(new HttpError(409, 'cannot put on directories'));
        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, {}));
            });
        }

        return next(new HttpError(500, 'unsupported type'));
    });
}

function del(req, res, next) {
    var filePath = req.params[0];
    var absoluteFilePath = getAbsolutePath(filePath);
    if (!absoluteFilePath) return next(new HttpError(404, 'Not found'));
    if (absoluteFilePath.slice(gBasePath.length) === '') return next(new HttpError(403, 'Forbidden'));

    fs.stat(absoluteFilePath, function (error, result) {
        if (error) return next(new HttpError(404, error));

        rm(absoluteFilePath, function (error, result) {
            if (error) return next(new HttpError(500, 'Unable to remove'));
            next(new HttpSuccess(200, { entries: result }));
        });
    });
}