2014-02-12 05:23:40 -05:00
logger = require ( " logger-sharelatex " )
fs = require ( " fs " )
request = require ( " request " )
settings = require ( " settings-sharelatex " )
2018-12-13 09:49:44 -05:00
Async = require ( ' async ' )
2019-05-13 04:39:07 -04:00
FileHashManager = require ( " ./FileHashManager " )
File = require ( ' ../../models/File ' ) . File
2014-02-12 05:23:40 -05:00
2014-05-08 05:55:44 -04:00
oneMinInMs = 60 * 1000
fiveMinsInMs = oneMinInMs * 5
2016-03-12 07:01:36 -05:00
module.exports = FileStoreHandler =
2014-02-12 05:23:40 -05:00
2018-12-13 09:49:44 -05:00
RETRY_ATTEMPTS: 3
2019-05-13 04:39:07 -04:00
uploadFileFromDisk: ( project_id , file_args , fsPath , callback = (error, url, fileRef) -> ) ->
2016-03-12 07:01:36 -05:00
fs . lstat fsPath , (err, stat)->
if err ?
2019-05-13 04:39:07 -04:00
logger . err err : err , project_id : project_id , file_args : file_args , fsPath : fsPath , " error stating file "
2016-03-12 10:05:29 -05:00
callback ( err )
2017-03-17 09:03:16 -04:00
if ! stat ?
2019-05-13 04:39:07 -04:00
logger . err project_id : project_id , file_args : file_args , fsPath : fsPath , " stat is not available, can not check file from disk "
2017-03-17 09:03:16 -04:00
return callback ( new Error ( " error getting stat, not available " ) )
2016-03-12 10:05:29 -05:00
if ! stat . isFile ( )
2019-05-13 04:39:07 -04:00
logger . log project_id : project_id , file_args : file_args , fsPath : fsPath , " tried to upload symlink, not contining "
2016-03-12 10:05:29 -05:00
return callback ( new Error ( " can not upload symlink " ) )
2019-05-13 04:39:07 -04:00
Async . retry FileStoreHandler . RETRY_ATTEMPTS , (cb, results) ->
FileStoreHandler . _doUploadFileFromDisk project_id , file_args , fsPath , cb
, (err, result) ->
2018-12-13 09:49:44 -05:00
if err ?
2019-05-13 04:39:07 -04:00
logger . err { err , project_id , file_args } , " Error uploading file, retries failed "
return callback ( err )
callback ( err , result . url , result . fileRef )
2016-03-12 10:05:29 -05:00
2019-05-13 04:39:07 -04:00
_doUploadFileFromDisk: ( project_id , file_args , fsPath , callback = (err, result)-> ) ->
2017-04-28 09:47:18 -04:00
_cb = callback
2019-05-13 04:39:07 -04:00
callback = (err, result...) ->
2017-04-28 09:47:18 -04:00
callback = -> # avoid double callbacks
2019-05-13 04:39:07 -04:00
_cb ( err , result . . . )
2017-04-28 09:47:18 -04:00
2019-05-13 04:39:07 -04:00
FileHashManager . computeHash fsPath , (err, hashValue) ->
return callback ( err ) if err ?
fileRef = new File ( Object . assign ( { } , file_args , { hash: hashValue } ) )
file_id = fileRef . _id
logger . log project_id : project_id , file_id : file_id , fsPath : fsPath , hash: hashValue , fileRef : fileRef , " uploading file from disk "
readStream = fs . createReadStream ( fsPath )
readStream . on " error " , (err)->
logger . err err : err , project_id : project_id , file_id : file_id , fsPath : fsPath , " something went wrong on the read stream of uploadFileFromDisk "
2017-04-28 09:47:18 -04:00
callback err
2019-05-13 04:39:07 -04:00
readStream . on " open " , () ->
url = FileStoreHandler . _buildUrl ( project_id , file_id )
opts =
method: " post "
uri: url
timeout : fiveMinsInMs
headers:
" X-File-Hash-From-Web " : hashValue # send the hash to the filestore as a custom header so it can be checked
writeStream = request ( opts )
writeStream . on " error " , (err)->
logger . err err : err , project_id : project_id , file_id : file_id , fsPath : fsPath , " something went wrong on the write stream of uploadFileFromDisk "
callback err
writeStream . on ' response ' , (response) ->
if response . statusCode not in [ 200 , 201 ]
err = new Error ( " non-ok response from filestore for upload: #{ response . statusCode } " )
logger . err { err , statusCode: response . statusCode } , " error uploading to filestore "
callback ( err )
else
callback ( null , { url , fileRef } ) # have to pass back an object because async.retry only accepts a single result argument
readStream . pipe writeStream
2014-02-12 05:23:40 -05:00
getFileStream: (project_id, file_id, query, callback)->
logger . log project_id : project_id , file_id : file_id , query : query , " getting file stream from file store "
queryString = " "
if query ? and query [ " format " ] ?
queryString = " ?format= #{ query [ ' format ' ] } "
opts =
method : " get "
uri: " #{ @ _buildUrl ( project_id , file_id ) } #{ queryString } "
2014-05-08 05:55:44 -04:00
timeout : fiveMinsInMs
2016-05-17 11:59:47 -04:00
headers: { }
if query ? and query [ ' range ' ] ?
rangeText = query [ ' range ' ]
if rangeText && rangeText . match ? && rangeText . match ( /\d+-\d+/ )
opts . headers [ ' range ' ] = " bytes= #{ query [ ' range ' ] } "
2014-02-12 05:23:40 -05:00
readStream = request ( opts )
2016-03-23 10:38:11 -04:00
readStream . on " error " , (err) ->
2019-03-12 07:35:18 -04:00
logger . err { err , project_id , file_id , query , opts } , " error in file stream "
2014-02-12 05:23:40 -05:00
callback ( null , readStream )
deleteFile: (project_id, file_id, callback)->
logger . log project_id : project_id , file_id : file_id , " telling file store to delete file "
opts =
method : " delete "
uri: @ _buildUrl ( project_id , file_id )
2014-05-08 05:55:44 -04:00
timeout : fiveMinsInMs
2014-02-12 05:23:40 -05:00
request opts , (err, response)->
if err ?
logger . err err : err , project_id : project_id , file_id : file_id , " something went wrong deleting file from filestore "
callback ( err )
copyFile: (oldProject_id, oldFile_id, newProject_id, newFile_id, callback)->
logger . log oldProject_id : oldProject_id , oldFile_id : oldFile_id , newProject_id : newProject_id , newFile_id : newFile_id , " telling filestore to copy a file "
opts =
method : " put "
json:
source:
project_id : oldProject_id
file_id : oldFile_id
uri: @ _buildUrl ( newProject_id , newFile_id )
2014-05-08 05:55:44 -04:00
timeout : fiveMinsInMs
2019-01-15 05:39:50 -05:00
request opts , (err, response)->
2014-02-12 05:23:40 -05:00
if err ?
logger . err err : err , oldProject_id : oldProject_id , oldFile_id : oldFile_id , newProject_id : newProject_id , newFile_id : newFile_id , " something went wrong telling filestore api to copy file "
2019-01-15 05:39:50 -05:00
callback ( err )
else if 200 <= response . statusCode < 300
# successful response
callback ( null , opts . uri )
else
err = new Error ( " non-ok response from filestore for copyFile: #{ response . statusCode } " )
logger . err { uri: opts . uri , statusCode: response . statusCode } , " error uploading to filestore "
callback ( err )
2014-02-12 05:23:40 -05:00
_buildUrl: (project_id, file_id)->
2017-03-09 05:11:45 -05:00
return " #{ settings . apis . filestore . url } /project/ #{ project_id } /file/ #{ file_id } "