logger = require("logger-sharelatex") fs = require("fs") request = require("request") settings = require("settings-sharelatex") oneMinInMs = 60 * 1000 fiveMinsInMs = oneMinInMs * 5 module.exports = FileStoreHandler = uploadFileFromDisk: (project_id, file_id, fsPath, callback)-> fs.lstat fsPath, (err, stat)-> if err? logger.err err:err, project_id:project_id, file_id:file_id, fsPath:fsPath, "error stating file" callback(err) if !stat? logger.err project_id:project_id, file_id:file_id, fsPath:fsPath, "stat is not available, can not check file from disk" return callback(new Error("error getting stat, not available")) if !stat.isFile() logger.log project_id:project_id, file_id:file_id, fsPath:fsPath, "tried to upload symlink, not contining" return callback(new Error("can not upload symlink")) _cb = callback callback = (err, url) -> callback = -> # avoid double callbacks _cb(err, url) logger.log project_id:project_id, file_id:file_id, fsPath:fsPath, "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" callback err readStream.on "open", () -> url = FileStoreHandler._buildUrl(project_id, file_id) opts = method: "post" uri: url timeout:fiveMinsInMs 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) readStream.pipe writeStream 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}" timeout:fiveMinsInMs headers: {} if query? and query['range']? rangeText = query['range'] if rangeText && rangeText.match? && rangeText.match(/\d+-\d+/) opts.headers['range'] = "bytes=#{query['range']}" readStream = request(opts) readStream.on "error", (err) -> logger.err {err, project_id, file_id, query}, "error in file stream" 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) timeout:fiveMinsInMs 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) timeout:fiveMinsInMs request opts, (err)-> 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" callback(err, opts.uri) _buildUrl: (project_id, file_id)-> return "#{settings.apis.filestore.url}/project/#{project_id}/file/#{file_id}"