/* eslint-disable camelcase, no-self-assign, no-unused-vars, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let FileHandler; const settings = require("settings-sharelatex"); const PersistorManager = require("./PersistorManager"); const LocalFileWriter = require("./LocalFileWriter"); const logger = require("logger-sharelatex"); const FileConverter = require("./FileConverter"); const KeyBuilder = require("./KeyBuilder"); const async = require("async"); const ImageOptimiser = require("./ImageOptimiser"); const Errors = require('./Errors'); module.exports = (FileHandler = { insertFile(bucket, key, stream, callback){ const convertedKey = KeyBuilder.getConvertedFolderKey(key); return PersistorManager.deleteDirectory(bucket, convertedKey, function(error) { if (error != null) { return callback(error); } return PersistorManager.sendStream(bucket, key, stream, callback); }); }, deleteFile(bucket, key, callback){ const convertedKey = KeyBuilder.getConvertedFolderKey(key); return async.parallel([ done => PersistorManager.deleteFile(bucket, key, done), done => PersistorManager.deleteDirectory(bucket, convertedKey, done) ], callback); }, getFile(bucket, key, opts, callback){ // In this call, opts can contain credentials if (opts == null) { opts = {}; } logger.log({bucket, key, opts:this._scrubSecrets(opts)}, "getting file"); if ((opts.format == null) && (opts.style == null)) { return this._getStandardFile(bucket, key, opts, callback); } else { return this._getConvertedFile(bucket, key, opts, callback); } }, getFileSize(bucket, key, callback) { return PersistorManager.getFileSize(bucket, key, callback); }, _getStandardFile(bucket, key, opts, callback){ return PersistorManager.getFileStream(bucket, key, opts, function(err, fileStream){ if ((err != null) && !(err instanceof Errors.NotFoundError)) { logger.err({bucket, key, opts:FileHandler._scrubSecrets(opts)}, "error getting fileStream"); } return callback(err, fileStream); }); }, _getConvertedFile(bucket, key, opts, callback){ const convertedKey = KeyBuilder.addCachingToKey(key, opts); return PersistorManager.checkIfFileExists(bucket, convertedKey, (err, exists)=> { if (err != null) { return callback(err); } if (exists) { return PersistorManager.getFileStream(bucket, convertedKey, opts, callback); } else { return this._getConvertedFileAndCache(bucket, key, convertedKey, opts, callback); } }); }, _getConvertedFileAndCache(bucket, key, convertedKey, opts, callback){ let convertedFsPath = ""; const originalFsPath = ""; return async.series([ cb => { return this._convertFile(bucket, key, opts, function(err, fileSystemPath, originalFsPath) { convertedFsPath = fileSystemPath; originalFsPath = originalFsPath; return cb(err); }); }, cb => ImageOptimiser.compressPng(convertedFsPath, cb), cb => PersistorManager.sendFile(bucket, convertedKey, convertedFsPath, cb) ], function(err){ if (err != null) { LocalFileWriter.deleteFile(convertedFsPath, function() {}); LocalFileWriter.deleteFile(originalFsPath, function() {}); return callback(err); } // Send back the converted file from the local copy to avoid problems // with the file not being present in S3 yet. As described in the // documentation below, we have already made a 'HEAD' request in // checkIfFileExists so we only have "eventual consistency" if we try // to stream it from S3 here. This was a cause of many 403 errors. // // "Amazon S3 provides read-after-write consistency for PUTS of new // objects in your S3 bucket in all regions with one caveat. The // caveat is that if you make a HEAD or GET request to the key name // (to find if the object exists) before creating the object, Amazon // S3 provides eventual consistency for read-after-write."" // https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyModel return LocalFileWriter.getStream(convertedFsPath, function(err, readStream) { if (err != null) { return callback(err); } readStream.on('end', function() { logger.log({convertedFsPath}, "deleting temporary file"); return LocalFileWriter.deleteFile(convertedFsPath, function() {}); }); return callback(null, readStream); }); }); }, _convertFile(bucket, originalKey, opts, callback){ return this._writeS3FileToDisk(bucket, originalKey, opts, function(err, originalFsPath){ if (err != null) { return callback(err); } const done = function(err, destPath){ if (err != null) { logger.err({err, bucket, originalKey, opts:FileHandler._scrubSecrets(opts)}, "error converting file"); return callback(err); } LocalFileWriter.deleteFile(originalFsPath, function() {}); return callback(err, destPath, originalFsPath); }; logger.log({opts}, "converting file depending on opts"); if (opts.format != null) { return FileConverter.convert(originalFsPath, opts.format, done); } else if (opts.style === "thumbnail") { return FileConverter.thumbnail(originalFsPath, done); } else if (opts.style === "preview") { return FileConverter.preview(originalFsPath, done); } else { return callback(new Error(`should have specified opts to convert file with ${JSON.stringify(opts)}`)); } }); }, _writeS3FileToDisk(bucket, key, opts, callback){ return PersistorManager.getFileStream(bucket, key, opts, function(err, fileStream){ if (err != null) { return callback(err); } return LocalFileWriter.writeStream(fileStream, key, callback); }); }, getDirectorySize(bucket, project_id, callback){ logger.log({bucket, project_id}, "getting project size"); return PersistorManager.directorySize(bucket, project_id, function(err, size){ if (err != null) { logger.err({bucket, project_id}, "error getting size"); } return callback(err, size); }); }, _scrubSecrets(opts){ const safe = Object.assign({}, opts); delete safe.credentials; return safe; } });