FileStoreHandler.coffee 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. logger = require("logger-sharelatex")
  2. fs = require("fs")
  3. request = require("request")
  4. settings = require("settings-sharelatex")
  5. Async = require('async')
  6. oneMinInMs = 60 * 1000
  7. fiveMinsInMs = oneMinInMs * 5
  8. module.exports = FileStoreHandler =
  9. RETRY_ATTEMPTS: 3
  10. uploadFileFromDisk: (project_id, file_id, fsPath, callback)->
  11. fs.lstat fsPath, (err, stat)->
  12. if err?
  13. logger.err err:err, project_id:project_id, file_id:file_id, fsPath:fsPath, "error stating file"
  14. callback(err)
  15. if !stat?
  16. logger.err project_id:project_id, file_id:file_id, fsPath:fsPath, "stat is not available, can not check file from disk"
  17. return callback(new Error("error getting stat, not available"))
  18. if !stat.isFile()
  19. logger.log project_id:project_id, file_id:file_id, fsPath:fsPath, "tried to upload symlink, not contining"
  20. return callback(new Error("can not upload symlink"))
  21. Async.retry FileStoreHandler.RETRY_ATTEMPTS, (cb) ->
  22. FileStoreHandler._doUploadFileFromDisk project_id, file_id, fsPath, cb
  23. , (err, url) ->
  24. if err?
  25. logger.err {err, project_id, file_id}, "Error uploading file, retries failed"
  26. callback(err, url)
  27. _doUploadFileFromDisk: (project_id, file_id, fsPath, callback) ->
  28. _cb = callback
  29. callback = (err, url) ->
  30. callback = -> # avoid double callbacks
  31. _cb(err, url)
  32. logger.log project_id:project_id, file_id:file_id, fsPath:fsPath, "uploading file from disk"
  33. readStream = fs.createReadStream(fsPath)
  34. readStream.on "error", (err)->
  35. logger.err err:err, project_id:project_id, file_id:file_id, fsPath:fsPath, "something went wrong on the read stream of uploadFileFromDisk"
  36. callback err
  37. readStream.on "open", () ->
  38. url = FileStoreHandler._buildUrl(project_id, file_id)
  39. opts =
  40. method: "post"
  41. uri: url
  42. timeout:fiveMinsInMs
  43. writeStream = request(opts)
  44. writeStream.on "error", (err)->
  45. logger.err err:err, project_id:project_id, file_id:file_id, fsPath:fsPath, "something went wrong on the write stream of uploadFileFromDisk"
  46. callback err
  47. writeStream.on 'response', (response) ->
  48. if response.statusCode not in [200, 201]
  49. err = new Error("non-ok response from filestore for upload: #{response.statusCode}")
  50. logger.err {err, statusCode: response.statusCode}, "error uploading to filestore"
  51. callback(err)
  52. else
  53. callback(null, url)
  54. readStream.pipe writeStream
  55. getFileStream: (project_id, file_id, query, callback)->
  56. logger.log project_id:project_id, file_id:file_id, query:query, "getting file stream from file store"
  57. queryString = ""
  58. if query? and query["format"]?
  59. queryString = "?format=#{query['format']}"
  60. opts =
  61. method : "get"
  62. uri: "#{@_buildUrl(project_id, file_id)}#{queryString}"
  63. timeout:fiveMinsInMs
  64. headers: {}
  65. if query? and query['range']?
  66. rangeText = query['range']
  67. if rangeText && rangeText.match? && rangeText.match(/\d+-\d+/)
  68. opts.headers['range'] = "bytes=#{query['range']}"
  69. readStream = request(opts)
  70. readStream.on "error", (err) ->
  71. logger.err {err, project_id, file_id, query}, "error in file stream"
  72. callback(null, readStream)
  73. deleteFile: (project_id, file_id, callback)->
  74. logger.log project_id:project_id, file_id:file_id, "telling file store to delete file"
  75. opts =
  76. method : "delete"
  77. uri: @_buildUrl(project_id, file_id)
  78. timeout:fiveMinsInMs
  79. request opts, (err, response)->
  80. if err?
  81. logger.err err:err, project_id:project_id, file_id:file_id, "something went wrong deleting file from filestore"
  82. callback(err)
  83. copyFile: (oldProject_id, oldFile_id, newProject_id, newFile_id, callback)->
  84. logger.log oldProject_id:oldProject_id, oldFile_id:oldFile_id, newProject_id:newProject_id, newFile_id:newFile_id, "telling filestore to copy a file"
  85. opts =
  86. method : "put"
  87. json:
  88. source:
  89. project_id:oldProject_id
  90. file_id:oldFile_id
  91. uri: @_buildUrl(newProject_id, newFile_id)
  92. timeout:fiveMinsInMs
  93. request opts, (err, response)->
  94. if err?
  95. 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"
  96. callback(err)
  97. else if 200 <= response.statusCode < 300
  98. # successful response
  99. callback(null, opts.uri)
  100. else
  101. err = new Error("non-ok response from filestore for copyFile: #{response.statusCode}")
  102. logger.err {uri: opts.uri, statusCode: response.statusCode}, "error uploading to filestore"
  103. callback(err)
  104. _buildUrl: (project_id, file_id)->
  105. return "#{settings.apis.filestore.url}/project/#{project_id}/file/#{file_id}"