webgme-engine
Version:
WebGME server and Client API without a GUI
237 lines (203 loc) • 8.91 kB
JavaScript
/*globals requireJS*/
/*eslint-env node*/
/**
* @author ksmyth / https://github.com/ksmyth
*/
;
var mime = require('mime'),
BlobMetadata = requireJS('blob/BlobMetadata'),
ASSERT = requireJS('common/util/assert'),
express = require('express'),
contentDisposition = require('content-disposition');
function createExpressBlob(options) {
var __app = express.Router(),
accessTokens,
BlobBackend,
blobBackend,
ensureAuthenticated,
getUserId,
logger;
ASSERT(typeof options.gmeConfig !== 'undefined', 'gmeConfig required');
ASSERT(typeof options.ensureAuthenticated === 'function', 'ensureAuthenticated must be given.');
accessTokens = options.accessTokens;
getUserId = options.getUserId;
logger = options.logger.fork('middleware:BlobServer');
ensureAuthenticated = async function (req, res, next) {
const {guestAccount, allowGuests} = options.gmeConfig.authentication;
await accessTokens.setUserFromToken(req, res);
const userId = getUserId(req);
const isGuest = userId === guestAccount;
const isInvalidUser = isGuest && !allowGuests;
if (!userId || isInvalidUser) {
return options.ensureAuthenticated(req, res, next);
}
return next();
};
if (options.gmeConfig.blob.type.toUpperCase() === 'FS') {
BlobBackend = require('./BlobFSBackend');
} else if (options.gmeConfig.blob.type.toUpperCase() === 'S3') {
BlobBackend = require('./BlobS3Backend');
} else {
throw new Error('Unknown blob type ' + options.gmeConfig.blob.type);
}
blobBackend = new BlobBackend(options.gmeConfig, logger);
/* debugging:
__app.use(function (req, res, next) {
var info = req.method + ' ' + req.url;
logger.debug(info);
var end = res.end;
res.end = function (chunk, encoding) {
res.end = end;
res.end(chunk, encoding);
logger.debug(info + ' => ' + res.statusCode);
};
next();
}); */
__app.get('/metadata', ensureAuthenticated, function (req, res) {
if (options.gmeConfig.debug || options.gmeConfig.blob.allowListAll) {
blobBackend.listAllMetadata(req.query.all, function (err, metadata) {
if (err) {
logger.error(err);
res.status(err.statusCode || 500);
res.send(err.message || err);
} else {
res.status(200);
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify(metadata, null, 4));
}
});
} else {
res.status(404);
res.send('Listing metadata only possible in debug mode.');
}
});
__app.get('/metadata/:metadataHash([0-9a-f]{40,40})', ensureAuthenticated, function (req, res) {
blobBackend.getMetadata(req.params.metadataHash, function (err, hash, metadata) {
if (err) {
logger.error(err);
res.status(err.statusCode || 500);
res.send(err.message || err);
} else {
res.status(200);
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify(metadata, null, 4));
}
});
});
__app.post('/createFile/:filename', ensureAuthenticated, function (req, res) {
logger.debug('file creation request: user[' + getUserId(req) + '], filename[' + req.params.filename + ']');
var filename = 'not_defined.txt';
if (req.params.filename !== null && req.params.filename !== '') {
filename = req.params.filename;
}
// regular file
// TODO: add tags and isPublic flag
blobBackend.putFile(filename, req, function (err, hash) {
logger.debug('file creation request finished: user[' + getUserId(req) + '], filename[' +
req.params.filename + '], error[' + err + '], hash:[' + hash + ']');
if (err) {
logger.error(err);
res.status(err.statusCode || 500);
res.send(err.message || err);
} else {
// FIXME: it should be enough to send back the hash only
blobBackend.getMetadata(hash, function (err, metadataHash, metadata) {
if (err) {
logger.error(err);
res.status(err.statusCode || 500);
res.send(err.message || err);
} else {
res.status(200);
res.setHeader('Content-type', 'application/json');
var info = {};
info[hash] = metadata;
res.end(JSON.stringify(info, null, 4));
}
});
}
});
});
__app.post('/createMetadata', ensureAuthenticated, function (req, res) {
var data = '';
req.addListener('data', function (chunk) {
data += chunk;
});
req.addListener('end', function () {
var metadata;
try {
metadata = new BlobMetadata(JSON.parse(data));
} catch (e) {
res.status(500);
res.send(e);
return;
}
blobBackend.putMetadata(metadata, function (err, hash) {
if (err) {
logger.error(err);
res.status(err.statusCode || 500);
res.send(err.message || err);
} else {
// FIXME: it should be enough to send back the hash only
blobBackend.getMetadata(hash, function (err, metadataHash, metadata) {
if (err) {
logger.error(err);
res.status(err.statusCode || 500);
res.send(err.message || err);
} else {
res.status(200);
res.setHeader('Content-type', 'application/json');
var info = {};
info[hash] = metadata;
res.end(JSON.stringify(info, null, 4));
}
});
}
});
});
});
var sendBlobContent = function (req, res, metadataHash, subpartPath, download) {
blobBackend.getMetadata(metadataHash, function (err, hash, metadata) {
if (err) {
res.status(err.statusCode || 500);
res.send(err.message || err);
} else {
var filename = metadata.name;
if (subpartPath) {
filename = subpartPath.substring(subpartPath.lastIndexOf('/') + 1);
}
// https://github.com/broofa/mime/issues/195
var mimeType = mime.getType(filename) || 'application/octet-stream';
if (download || mimeType === 'application/octet-stream' || mimeType === 'application/zip') {
res.setHeader('Content-Disposition', contentDisposition(filename, {type: 'attachment'}));
}
res.setHeader('Content-type', mimeType);
// TODO: we need to get the content and save as a local file.
// if we just proxy the stream we cannot set errors correctly.
blobBackend.getFile(metadataHash, subpartPath, res, function (err /*, hash*/) {
if (err) {
// chrome gives error code: ERR_INVALID_RESPONSE if we don't do this:
res.removeHeader('Content-disposition');
res.removeHeader('Content-type');
//give more precise description about the error type and message. Resource if not available etc.
res.sendStatus(500);
} else {
//res.status(200);
}
});
}
});
};
__app.get(/^\/download\/([0-9a-f]{40,40})(\/(.*))?$/, ensureAuthenticated, function (req, res) {
var metadataHash = req.params[0];
var subpartPath = req.params[2];
sendBlobContent(req, res, metadataHash, subpartPath, true);
});
__app.get(/^\/view\/([0-9a-f]{40,40})(\/(.*))?$/, ensureAuthenticated, function (req, res) {
var metadataHash = req.params[0];
var subpartPath = req.params[2];
sendBlobContent(req, res, metadataHash, subpartPath, false);
});
// end of blob rules
return __app;
}
module.exports.createExpressBlob = createExpressBlob;