зеркало из https://github.com/mozilla/makedrive.git
Add /z/* route for export, make routes optional, add tests for /z /j/ /p routes
This commit is contained in:
Родитель
611a6869e5
Коммит
08beb186b0
|
@ -6,4 +6,4 @@ before_install:
|
|||
before_script:
|
||||
- grunt init
|
||||
env:
|
||||
- "PORT=9090 ALLOWED_CORS_DOMAINS=\"http://localhost:7777\" NODE_ENV=\"development\" ENABLE_GELF_LOGS=false SESSION_SECRET=\"secret value\" FORCE_SSL=false LOGIN_SERVER_URL_WITH_AUTH=\"http://localhost:3000\""
|
||||
- "PORT=9090 ALLOWED_CORS_DOMAINS=\"http://localhost:7777\" NODE_ENV=\"development\" ENABLE_GELF_LOGS=false SESSION_SECRET=\"secret value\" FORCE_SSL=false LOGIN_SERVER_URL_WITH_AUTH=\"http://localhost:3000\" ENABLE_PATH_ROUTE=true ENABLE_JSON_ROUTE=true ENABLE_ZIP_ROUTE=true"
|
||||
|
|
8
env.dist
8
env.dist
|
@ -11,6 +11,14 @@ export PORT=9090
|
|||
# Add domains that we allow (e.g., for /api/sync route)
|
||||
export ALLOWED_CORS_DOMAINS='["http://localhost:7777", "http://localhost:5001"]'
|
||||
|
||||
# Various optional HTTP routes for getting data out of MakeDrive. Set to false to turn off.
|
||||
# /p/path/into/filesystem -> serves path like Apache would
|
||||
export ENABLE_PATH_ROUTE=true
|
||||
# /j/path/into/filesystem -> serves path as JSON for API consumption
|
||||
export ENABLE_JSON_ROUTE=true
|
||||
# /z/path/into/filesystem -> serves path as .zip file for export
|
||||
export ENABLE_ZIP_ROUTE=true
|
||||
|
||||
# AWS-S3 information
|
||||
export S3_BUCKET="org.webmadecontent.staging.makedrive"
|
||||
export S3_KEY=
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"url": "https://github.com/mozilla/makedrive/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"archiver": "^0.10.1",
|
||||
"MD5": "^1.2.1",
|
||||
"async": "^0.9.0",
|
||||
"webmaker-auth": "0.0.14",
|
||||
|
|
|
@ -17,16 +17,7 @@ function write(content, contentType, res, status) {
|
|||
* Send an Apache-style 404
|
||||
*/
|
||||
function handle404(url, res) {
|
||||
var html = '<!DOCTYPE html>' +
|
||||
'<html><head>' +
|
||||
'<title>404 Not Found</title>' +
|
||||
'</head><body>' +
|
||||
'<h1>Not Found</h1>' +
|
||||
'<p>The requested URL ' + url + ' was not found on this server.</p>' +
|
||||
'<hr>' +
|
||||
'<address>MakeDrive/' + version + ' (Web) Server</address>' +
|
||||
'</body></html>';
|
||||
write(html, 'text/html', res, 404);
|
||||
util.standard404(url, res);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
var filesystem = require('../filesystem.js');
|
||||
var DefaultHandler = require('./default-handler.js');
|
||||
var JSONHandler = require('./json-handler.js');
|
||||
var ZIPHandler = require('./zip-handler.js');
|
||||
|
||||
function FilerWebServer(username, res, options) {
|
||||
options = options || {};
|
||||
|
@ -17,6 +18,8 @@ function FilerWebServer(username, res, options) {
|
|||
// Pick the appropriate handler type to create
|
||||
if(options.json) {
|
||||
this.handler = new JSONHandler(fs, res);
|
||||
} else if(options.zip) {
|
||||
this.handler = new ZIPHandler(fs, res);
|
||||
} else {
|
||||
this.handler = new DefaultHandler(fs, res);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
var version = require('../../../package.json').version;
|
||||
|
||||
function formatDate(d) {
|
||||
// 20-Apr-2004 17:14
|
||||
return d.getDay() + '-' +
|
||||
|
@ -41,9 +43,24 @@ function isImage(ext) {
|
|||
ext === '.ico';
|
||||
}
|
||||
|
||||
function standard404(url, res) {
|
||||
var html = '<!DOCTYPE html>' +
|
||||
'<html><head>' +
|
||||
'<title>404 Not Found</title>' +
|
||||
'</head><body>' +
|
||||
'<h1>Not Found</h1>' +
|
||||
'<p>The requested URL ' + url + ' was not found on this server.</p>' +
|
||||
'<hr>' +
|
||||
'<address>MakeDrive/' + version + ' (Web) Server</address>' +
|
||||
'</body></html>';
|
||||
res.header({'Content-Type': 'text/html'});
|
||||
res.send(404, html);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatDate: formatDate,
|
||||
formatSize: formatSize,
|
||||
isMedia: isMedia,
|
||||
isImage: isImage
|
||||
isImage: isImage,
|
||||
standard404: standard404
|
||||
};
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* A ZIP Handler, for exporting filesystem contents.
|
||||
* The generated zip archive roots all files/dirs in
|
||||
* an export/ folder, and returns export.zip.
|
||||
*/
|
||||
var archiver = require('archiver');
|
||||
var Path = require('../../../lib/filer.js').Path;
|
||||
var async = require('async');
|
||||
var util = require('./util.js');
|
||||
|
||||
function archivePath(fs, path, res) {
|
||||
function fixPath(path) {
|
||||
// Make path relative within the zip archive
|
||||
return path.replace(/^\//, 'export/');
|
||||
}
|
||||
|
||||
function addFile(path, callback) {
|
||||
fs.readFile(path, function(err, data) {
|
||||
if(err) return callback(err);
|
||||
|
||||
archive.append(data, {name: fixPath(path)});
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function addDir(path, callback) {
|
||||
fs.readdir(path, function(err, list) {
|
||||
if(err) return callback(err);
|
||||
|
||||
// Add the directory itself
|
||||
archive.append(null, {name: fixPath(path) + '/'});
|
||||
|
||||
// Add all children of this dir, too
|
||||
async.eachSeries(list, function(entry, callback) {
|
||||
add(Path.join(path, entry), callback);
|
||||
}, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function add(path, callback) {
|
||||
fs.stat(path, function(err, stats) {
|
||||
if(err) return callback(err);
|
||||
|
||||
if(stats.isDirectory()) {
|
||||
addDir(path, callback);
|
||||
} else {
|
||||
addFile(path, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function error() {
|
||||
// Signal to the client that things are broken by hanging up.
|
||||
// There may be a better way to handle the error case here.
|
||||
if(res.socket) {
|
||||
res.socket.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
res.header('Content-Type', 'application/zip');
|
||||
res.header('Content-Disposition', 'attachment; filename=export.zip');
|
||||
|
||||
var archive = archiver('zip');
|
||||
archive.on('error', error);
|
||||
archive.pipe(res);
|
||||
|
||||
add(path, function(err) {
|
||||
if(err) {
|
||||
error();
|
||||
} else {
|
||||
archive.finalize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function ZIPHandler(fs, res) {
|
||||
this.fs = fs;
|
||||
this.res = res;
|
||||
}
|
||||
|
||||
ZIPHandler.prototype.handle404 = function(path) {
|
||||
util.standard404(path, this.res);
|
||||
};
|
||||
|
||||
ZIPHandler.prototype.handleDir = function(path) {
|
||||
archivePath(this.fs, path, this.res);
|
||||
};
|
||||
|
||||
ZIPHandler.prototype.handleFile = function(path) {
|
||||
archivePath(this.fs, path, this.res);
|
||||
};
|
||||
|
||||
module.exports = ZIPHandler;
|
|
@ -30,15 +30,26 @@ module.exports = function createRoutes( app, webmakerAuth ) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a path as JSON (for APIs) from a user's Filer filesystem
|
||||
*/
|
||||
setupWWWRoutes('/j/*', {json: true});
|
||||
|
||||
/**
|
||||
* Serve a path from a user's Filer filesystem
|
||||
*/
|
||||
setupWWWRoutes('/p/*', null);
|
||||
if(env.get('ENABLE_PATH_ROUTE')) {
|
||||
setupWWWRoutes('/p/*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a path as JSON (for APIs) from a user's Filer filesystem
|
||||
*/
|
||||
if(env.get('ENABLE_JSON_ROUTE')) {
|
||||
setupWWWRoutes('/j/*', {json: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a path as a .zip (for export) from a user's Filer filesystem
|
||||
*/
|
||||
if(env.get('ENABLE_ZIP_ROUTE')) {
|
||||
setupWWWRoutes('/z/*', {zip: true});
|
||||
}
|
||||
|
||||
app.get( "/api/sync", middleware.crossOriginHandler, middleware.authenticationHandler, function( req, res ) {
|
||||
var username = req.params.username;
|
||||
|
|
|
@ -1,32 +1,215 @@
|
|||
var expect = require('chai').expect;
|
||||
var request = require('request');
|
||||
var util = require('../lib/util');
|
||||
// Ensure the client timeout restricts tests to a reasonable length
|
||||
var Filer = require('../../lib/filer.js');
|
||||
var FileSystem = Filer.FileSystem;
|
||||
var Path = Filer.Path;
|
||||
var env = require('../../server/lib/environment');
|
||||
env.set('ALLOWED_CORS_DOMAINS', util.serverURL);
|
||||
var ALLOW_DOMAINS = process.env.ALLOWED_CORS_DOMAINS;
|
||||
|
||||
describe('[HTTP route tests]', function() {
|
||||
|
||||
it('should allow CORS access to /api/sync route', function(done) {
|
||||
request.get(util.serverURL + '/api/sync', { headers: {origin: ALLOW_DOMAINS }}, function(req, res, body) {
|
||||
expect(ALLOW_DOMAINS).to.contain(res.headers['access-control-allow-origin']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('/p/ should return a 404 error page if the path is not recognized', function(done) {
|
||||
util.authenticate(function(err, result) {
|
||||
expect(err).not.to.exist;
|
||||
expect(result.jar).to.exist;
|
||||
|
||||
request.get({
|
||||
url: util.serverURL + '/p/no/file/here.html',
|
||||
jar: result.jar
|
||||
}, function(err, res, body) {
|
||||
describe('/p/ route tests', function() {
|
||||
it('should return a 404 error page if the path is not recognized', function(done) {
|
||||
util.authenticate(function(err, result) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(404);
|
||||
expect(body).to.match(/<title>404 Not Found<\/title>/);
|
||||
expect(body).to.match(/The requested URL \/no\/file\/here.html was not found on this server./);
|
||||
done();
|
||||
expect(result.jar).to.exist;
|
||||
|
||||
request.get({
|
||||
url: util.serverURL + '/p/no/file/here.html',
|
||||
jar: result.jar
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(404);
|
||||
expect(body).to.match(/<title>404 Not Found<\/title>/);
|
||||
expect(body).to.match(/The requested URL \/no\/file\/here.html was not found on this server./);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the contents of a file if the path is valid', function(done) {
|
||||
var username = util.username();
|
||||
var content = "This is the content of the file.";
|
||||
|
||||
util.upload(username, '/index.html', content, function(err) {
|
||||
if(err) throw err;
|
||||
|
||||
util.authenticate({username: username}, function(err, result) {
|
||||
if(err) throw err;
|
||||
|
||||
// /p/index.html should come back as uploaded
|
||||
request.get({
|
||||
url: util.serverURL + '/p/index.html',
|
||||
jar: result.jar
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(200);
|
||||
expect(body).to.equal(content);
|
||||
|
||||
// /p/ should come back with dir listing
|
||||
request.get({
|
||||
url: util.serverURL + '/p/',
|
||||
jar: result.jar
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(200);
|
||||
// Look for artifacts we'd expect in the directory listing
|
||||
expect(body).to.match(/<head><title>Index of \/<\/title>/);
|
||||
expect(body).to.match(/<a href="\/p\/index.html">index.html<\/a>/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('/j/ route tests', function() {
|
||||
it('should return a 404 error page if the path is not recognized', function(done) {
|
||||
util.authenticate(function(err, result) {
|
||||
expect(err).not.to.exist;
|
||||
expect(result.jar).to.exist;
|
||||
|
||||
request.get({
|
||||
url: util.serverURL + '/j/no/file/here.html',
|
||||
jar: result.jar,
|
||||
json: true
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(404);
|
||||
expect(body.error.code).to.equal(404);
|
||||
expect(body.error.message).to.match(/The requested URL \/no\/file\/here.html was not found on this server./);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the contents of a file if the path is valid', function(done) {
|
||||
var username = util.username();
|
||||
var content = "This is the content of the file.";
|
||||
|
||||
util.upload(username, '/index.html', content, function(err) {
|
||||
if(err) throw err;
|
||||
|
||||
util.authenticate({username: username}, function(err, result) {
|
||||
if(err) throw err;
|
||||
|
||||
// /j/index.html should come back as JSON
|
||||
request.get({
|
||||
url: util.serverURL + '/j/index.html',
|
||||
jar: result.jar,
|
||||
json: true
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(200);
|
||||
expect(body).to.equal(content);
|
||||
|
||||
// /j/ should come back with dir listing
|
||||
request.get({
|
||||
url: util.serverURL + '/j/',
|
||||
jar: result.jar,
|
||||
json: true
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(200);
|
||||
|
||||
/**
|
||||
* We expect JSON something like this:
|
||||
* [{path: 'index.html',
|
||||
* links: 1,
|
||||
* size: 32,
|
||||
* modified: 1407336648736,
|
||||
* type: 'FILE'}]
|
||||
*/
|
||||
expect(body.length).to.equal(1);
|
||||
expect(body[0].path).to.equal('index.html');
|
||||
expect(body[0].links).to.equal(1);
|
||||
expect(body[0].size).to.be.a.number;
|
||||
expect(body[0].modified).to.be.a.number;
|
||||
expect(body[0].type).to.equal('FILE');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/z/ route tests', function() {
|
||||
it('should return a 404 error page if the path is not recognized', function(done) {
|
||||
util.authenticate(function(err, result) {
|
||||
expect(err).not.to.exist;
|
||||
expect(result.jar).to.exist;
|
||||
|
||||
request.get({
|
||||
url: util.serverURL + '/z/no/file/here.html',
|
||||
jar: result.jar
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(404);
|
||||
expect(body).to.match(/<title>404 Not Found<\/title>/);
|
||||
expect(body).to.match(/The requested URL \/no\/file\/here.html was not found on this server./);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return export.zip for a valid path', function(done) {
|
||||
var username = util.username();
|
||||
var content = "This is the content of the file.";
|
||||
|
||||
util.upload(username, '/index.html', content, function(err) {
|
||||
if(err) throw err;
|
||||
|
||||
util.authenticate({username: username}, function(err, result) {
|
||||
if(err) throw err;
|
||||
|
||||
// /z/ should come back as export.zip with one dir and file
|
||||
// in the archive.
|
||||
request.get({
|
||||
url: util.serverURL + '/z/',
|
||||
jar: result.jar,
|
||||
encoding: null
|
||||
}, function(err, res, body) {
|
||||
expect(err).not.to.exist;
|
||||
expect(res.statusCode).to.equal(200);
|
||||
expect(res.headers['content-type']).to.equal('application/zip');
|
||||
expect(res.headers['content-disposition']).to.equal('attachment; filename=export.zip');
|
||||
|
||||
// Write the zip file to filer, unzip, and compare file to original
|
||||
var fs = new FileSystem({provider: new FileSystem.providers.Memory(username)});
|
||||
var sh = fs.Shell();
|
||||
|
||||
sh.tempDir(function(err, tmp) {
|
||||
if(err) throw err;
|
||||
|
||||
fs.writeFile('/exports.zip', body, function(err) {
|
||||
if(err) throw err;
|
||||
|
||||
sh.unzip('/exports.zip', { destination: tmp }, function(err) {
|
||||
if(err) throw err;
|
||||
|
||||
fs.readFile(Path.join(tmp, 'export/index.html'), 'utf8', function(err, data) {
|
||||
if(err) throw err;
|
||||
expect(data).to.equal(content);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче