2019-12-16 05:24:37 -05:00
|
|
|
/* 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.
|
2019-12-16 05:24:35 -05:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2019-12-16 05:42:31 -05:00
|
|
|
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')
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
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
|
|
|
|
)
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
getFileSize(bucket, key, callback) {
|
|
|
|
return PersistorManager.getFileSize(bucket, key, callback)
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
_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)
|
|
|
|
})
|
|
|
|
},
|
2019-06-13 16:57:49 -04:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
_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
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
_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)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
_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)
|
|
|
|
}
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
logger.log({ opts }, 'converting file depending on opts')
|
2014-03-04 08:36:47 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
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
|
|
|
|
)}`
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
_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)
|
|
|
|
})
|
|
|
|
},
|
2014-02-14 11:39:05 -05:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
},
|
2016-03-13 15:22:14 -04:00
|
|
|
|
2019-12-16 05:42:31 -05:00
|
|
|
_scrubSecrets(opts) {
|
|
|
|
const safe = Object.assign({}, opts)
|
|
|
|
delete safe.credentials
|
|
|
|
return safe
|
|
|
|
}
|
|
|
|
}
|