ProjectOutputFileAgent.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /* eslint-disable
  2. camelcase,
  3. node/handle-callback-err,
  4. max-len,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS207: Consider shorter variations of null checks
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. let ProjectOutputFileAgent
  16. const AuthorizationManager = require('../Authorization/AuthorizationManager')
  17. const ProjectGetter = require('../Project/ProjectGetter')
  18. const Settings = require('@overleaf/settings')
  19. const CompileManager = require('../Compile/CompileManager')
  20. const ClsiManager = require('../Compile/ClsiManager')
  21. const ProjectFileAgent = require('./ProjectFileAgent')
  22. const _ = require('underscore')
  23. const {
  24. BadDataError,
  25. AccessDeniedError,
  26. BadEntityTypeError,
  27. OutputFileFetchFailedError,
  28. } = require('./LinkedFilesErrors')
  29. const LinkedFilesHandler = require('./LinkedFilesHandler')
  30. const logger = require('logger-sharelatex')
  31. module.exports = ProjectOutputFileAgent = {
  32. _prepare(project_id, linkedFileData, user_id, callback) {
  33. if (callback == null) {
  34. callback = function (err, linkedFileData) {}
  35. }
  36. return this._checkAuth(
  37. project_id,
  38. linkedFileData,
  39. user_id,
  40. (err, allowed) => {
  41. if (err != null) {
  42. return callback(err)
  43. }
  44. if (!allowed) {
  45. return callback(new AccessDeniedError())
  46. }
  47. if (!this._validate(linkedFileData)) {
  48. return callback(new BadDataError())
  49. }
  50. return callback(null, linkedFileData)
  51. }
  52. )
  53. },
  54. createLinkedFile(
  55. project_id,
  56. linkedFileData,
  57. name,
  58. parent_folder_id,
  59. user_id,
  60. callback
  61. ) {
  62. if (!this._canCreate(linkedFileData)) {
  63. return callback(new AccessDeniedError())
  64. }
  65. linkedFileData = this._sanitizeData(linkedFileData)
  66. return this._prepare(
  67. project_id,
  68. linkedFileData,
  69. user_id,
  70. (err, linkedFileData) => {
  71. if (err != null) {
  72. return callback(err)
  73. }
  74. return this._getFileStream(
  75. linkedFileData,
  76. user_id,
  77. (err, readStream) => {
  78. if (err != null) {
  79. return callback(err)
  80. }
  81. readStream.on('error', callback)
  82. return readStream.on('response', response => {
  83. if (response.statusCode >= 200 && response.statusCode < 300) {
  84. readStream.resume()
  85. return LinkedFilesHandler.importFromStream(
  86. project_id,
  87. readStream,
  88. linkedFileData,
  89. name,
  90. parent_folder_id,
  91. user_id,
  92. function (err, file) {
  93. if (err != null) {
  94. return callback(err)
  95. }
  96. return callback(null, file._id)
  97. }
  98. ) // Created
  99. } else {
  100. err = new OutputFileFetchFailedError(
  101. `Output file fetch failed: ${linkedFileData.build_id}, ${linkedFileData.source_output_file_path}`
  102. )
  103. err.statusCode = response.statusCode
  104. return callback(err)
  105. }
  106. })
  107. }
  108. )
  109. }
  110. )
  111. },
  112. refreshLinkedFile(
  113. project_id,
  114. linkedFileData,
  115. name,
  116. parent_folder_id,
  117. user_id,
  118. callback
  119. ) {
  120. return this._prepare(
  121. project_id,
  122. linkedFileData,
  123. user_id,
  124. (err, linkedFileData) => {
  125. if (err != null) {
  126. return callback(err)
  127. }
  128. return this._compileAndGetFileStream(
  129. linkedFileData,
  130. user_id,
  131. (err, readStream, new_build_id) => {
  132. if (err != null) {
  133. return callback(err)
  134. }
  135. readStream.on('error', callback)
  136. return readStream.on('response', response => {
  137. if (response.statusCode >= 200 && response.statusCode < 300) {
  138. readStream.resume()
  139. linkedFileData.build_id = new_build_id
  140. return LinkedFilesHandler.importFromStream(
  141. project_id,
  142. readStream,
  143. linkedFileData,
  144. name,
  145. parent_folder_id,
  146. user_id,
  147. function (err, file) {
  148. if (err != null) {
  149. return callback(err)
  150. }
  151. return callback(null, file._id)
  152. }
  153. ) // Created
  154. } else {
  155. err = new OutputFileFetchFailedError(
  156. `Output file fetch failed: ${linkedFileData.build_id}, ${linkedFileData.source_output_file_path}`
  157. )
  158. err.statusCode = response.statusCode
  159. return callback(err)
  160. }
  161. })
  162. }
  163. )
  164. }
  165. )
  166. },
  167. _sanitizeData(data) {
  168. return {
  169. provider: data.provider,
  170. source_project_id: data.source_project_id,
  171. source_output_file_path: data.source_output_file_path,
  172. build_id: data.build_id,
  173. }
  174. },
  175. _canCreate: ProjectFileAgent._canCreate,
  176. _getSourceProject: LinkedFilesHandler.getSourceProject,
  177. _validate(data) {
  178. if (data.v1_source_doc_id != null) {
  179. return (
  180. data.v1_source_doc_id != null && data.source_output_file_path != null
  181. )
  182. } else {
  183. return (
  184. data.source_project_id != null &&
  185. data.source_output_file_path != null &&
  186. data.build_id != null
  187. )
  188. }
  189. },
  190. _checkAuth(project_id, data, current_user_id, callback) {
  191. if (callback == null) {
  192. callback = function (err, allowed) {}
  193. }
  194. callback = _.once(callback)
  195. if (!this._validate(data)) {
  196. return callback(new BadDataError())
  197. }
  198. return this._getSourceProject(data, function (err, project) {
  199. if (err != null) {
  200. return callback(err)
  201. }
  202. return AuthorizationManager.canUserReadProject(
  203. current_user_id,
  204. project._id,
  205. null,
  206. function (err, canRead) {
  207. if (err != null) {
  208. return callback(err)
  209. }
  210. return callback(null, canRead)
  211. }
  212. )
  213. })
  214. },
  215. _getFileStream(linkedFileData, user_id, callback) {
  216. if (callback == null) {
  217. callback = function (err, fileStream) {}
  218. }
  219. callback = _.once(callback)
  220. const { source_output_file_path, build_id } = linkedFileData
  221. return this._getSourceProject(linkedFileData, function (err, project) {
  222. if (err != null) {
  223. return callback(err)
  224. }
  225. const source_project_id = project._id
  226. return ClsiManager.getOutputFileStream(
  227. source_project_id,
  228. user_id,
  229. build_id,
  230. source_output_file_path,
  231. function (err, readStream) {
  232. if (err != null) {
  233. return callback(err)
  234. }
  235. readStream.pause()
  236. return callback(null, readStream)
  237. }
  238. )
  239. })
  240. },
  241. _compileAndGetFileStream(linkedFileData, user_id, callback) {
  242. if (callback == null) {
  243. callback = function (err, stream, build_id) {}
  244. }
  245. callback = _.once(callback)
  246. const { source_output_file_path } = linkedFileData
  247. return this._getSourceProject(linkedFileData, function (err, project) {
  248. if (err != null) {
  249. return callback(err)
  250. }
  251. const source_project_id = project._id
  252. return CompileManager.compile(
  253. source_project_id,
  254. user_id,
  255. {},
  256. function (err, status, outputFiles) {
  257. if (err != null) {
  258. return callback(err)
  259. }
  260. if (status !== 'success') {
  261. return callback(new OutputFileFetchFailedError())
  262. }
  263. const outputFile = _.find(
  264. outputFiles,
  265. o => o.path === source_output_file_path
  266. )
  267. if (outputFile == null) {
  268. return callback(new OutputFileFetchFailedError())
  269. }
  270. const build_id = outputFile.build
  271. return ClsiManager.getOutputFileStream(
  272. source_project_id,
  273. user_id,
  274. build_id,
  275. source_output_file_path,
  276. function (err, readStream) {
  277. if (err != null) {
  278. return callback(err)
  279. }
  280. readStream.pause()
  281. return callback(null, readStream, build_id)
  282. }
  283. )
  284. }
  285. )
  286. })
  287. },
  288. }