ProjectOutputFileAgent.mjs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import AuthorizationManager from '../Authorization/AuthorizationManager.mjs'
  2. import CompileManager from '../Compile/CompileManager.mjs'
  3. import ClsiManager from '../Compile/ClsiManager.mjs'
  4. import ProjectFileAgent from './ProjectFileAgent.mjs'
  5. import _ from 'lodash'
  6. import LinkedFilesErrors from './LinkedFilesErrors.mjs'
  7. import { OutputFileFetchFailedError } from '../Errors/Errors.js'
  8. import LinkedFilesHandler from './LinkedFilesHandler.mjs'
  9. import { promisify } from '@overleaf/promise-utils'
  10. const { CompileFailedError, BadDataError, AccessDeniedError } =
  11. LinkedFilesErrors
  12. function _prepare(projectId, linkedFileData, userId, callback) {
  13. _checkAuth(projectId, linkedFileData, userId, (err, allowed) => {
  14. if (err) {
  15. return callback(err)
  16. }
  17. if (!allowed) {
  18. return callback(new AccessDeniedError())
  19. }
  20. if (!_validate(linkedFileData)) {
  21. return callback(new BadDataError())
  22. }
  23. callback(null, linkedFileData)
  24. })
  25. }
  26. function createLinkedFile(
  27. projectId,
  28. linkedFileData,
  29. name,
  30. parentFolderId,
  31. userId,
  32. callback
  33. ) {
  34. if (!ProjectFileAgent._canCreate(linkedFileData)) {
  35. return callback(new AccessDeniedError())
  36. }
  37. linkedFileData = _sanitizeData(linkedFileData)
  38. _prepare(projectId, linkedFileData, userId, (err, linkedFileData) => {
  39. if (err) {
  40. return callback(err)
  41. }
  42. _getFileStream(linkedFileData, userId, (err, readStream) => {
  43. if (err) {
  44. return callback(err)
  45. }
  46. LinkedFilesHandler.importFromStream(
  47. projectId,
  48. readStream,
  49. linkedFileData,
  50. name,
  51. parentFolderId,
  52. userId,
  53. (err, file) => {
  54. if (err) {
  55. return callback(err)
  56. }
  57. callback(null, file._id)
  58. }
  59. )
  60. })
  61. })
  62. }
  63. function refreshLinkedFile(
  64. projectId,
  65. linkedFileData,
  66. name,
  67. parentFolderId,
  68. userId,
  69. callback
  70. ) {
  71. _prepare(projectId, linkedFileData, userId, (err, linkedFileData) => {
  72. if (err) {
  73. return callback(err)
  74. }
  75. _compileAndGetFileStream(
  76. linkedFileData,
  77. userId,
  78. (err, readStream, newBuildId) => {
  79. if (err) {
  80. return callback(err)
  81. }
  82. linkedFileData.build_id = newBuildId
  83. LinkedFilesHandler.importFromStream(
  84. projectId,
  85. readStream,
  86. linkedFileData,
  87. name,
  88. parentFolderId,
  89. userId,
  90. (err, file) => {
  91. if (err) {
  92. return callback(err)
  93. }
  94. callback(null, file._id)
  95. }
  96. )
  97. }
  98. )
  99. })
  100. }
  101. function _sanitizeData(data) {
  102. return {
  103. provider: data.provider,
  104. source_project_id: data.source_project_id,
  105. source_output_file_path: data.source_output_file_path,
  106. build_id: data.build_id,
  107. clsiServerId: data.clsiServerId,
  108. importedAt: data.importedAt,
  109. }
  110. }
  111. function _validate(data) {
  112. return (
  113. (data.v1_source_doc_id != null && data.source_output_file_path != null) ||
  114. (data.source_project_id != null &&
  115. data.source_output_file_path != null &&
  116. data.build_id != null)
  117. )
  118. }
  119. function _checkAuth(projectId, data, currentUserId, callback) {
  120. callback = _.once(callback)
  121. if (!_validate(data)) {
  122. return callback(new BadDataError())
  123. }
  124. LinkedFilesHandler.getSourceProject(data, (err, project) => {
  125. if (err) {
  126. return callback(err)
  127. }
  128. AuthorizationManager.canUserReadProject(
  129. currentUserId,
  130. project._id,
  131. null,
  132. (err, canRead) => {
  133. if (err) {
  134. return callback(err)
  135. }
  136. callback(null, canRead)
  137. }
  138. )
  139. })
  140. }
  141. function _getFileStream(linkedFileData, userId, callback) {
  142. callback = _.once(callback)
  143. const {
  144. source_output_file_path: sourceOutputFilePath,
  145. build_id: buildId,
  146. clsiServerId,
  147. } = linkedFileData
  148. LinkedFilesHandler.getSourceProject(linkedFileData, (err, project) => {
  149. if (err) {
  150. return callback(err)
  151. }
  152. const sourceProjectId = project._id
  153. CompileManager.getProjectCompileLimits(sourceProjectId, (err, limits) => {
  154. if (err) return callback(err)
  155. ClsiManager.getOutputFileStream(
  156. sourceProjectId,
  157. userId,
  158. limits,
  159. clsiServerId,
  160. buildId,
  161. sourceOutputFilePath,
  162. (err, readStream) => {
  163. if (err) {
  164. return callback(err)
  165. }
  166. callback(null, readStream)
  167. }
  168. )
  169. })
  170. })
  171. }
  172. function _compileAndGetFileStream(linkedFileData, userId, callback) {
  173. callback = _.once(callback)
  174. const { source_output_file_path: sourceOutputFilePath } = linkedFileData
  175. LinkedFilesHandler.getSourceProject(linkedFileData, (err, project) => {
  176. if (err) {
  177. return callback(err)
  178. }
  179. const sourceProjectId = project._id
  180. CompileManager.compile(
  181. sourceProjectId,
  182. userId,
  183. {},
  184. (err, status, outputFiles, clsiServerId, limits) => {
  185. if (err) {
  186. return callback(err)
  187. }
  188. if (status !== 'success') {
  189. return callback(new CompileFailedError())
  190. }
  191. const outputFile = _.find(
  192. outputFiles,
  193. o => o.path === sourceOutputFilePath
  194. )
  195. if (outputFile == null) {
  196. return callback(new OutputFileFetchFailedError())
  197. }
  198. const buildId = outputFile.build
  199. ClsiManager.getOutputFileStream(
  200. sourceProjectId,
  201. userId,
  202. limits,
  203. clsiServerId,
  204. buildId,
  205. sourceOutputFilePath,
  206. (err, readStream) => {
  207. if (err) {
  208. return callback(err)
  209. }
  210. callback(null, readStream, buildId)
  211. }
  212. )
  213. }
  214. )
  215. })
  216. }
  217. export default {
  218. createLinkedFile,
  219. refreshLinkedFile,
  220. promises: {
  221. createLinkedFile: promisify(createLinkedFile),
  222. refreshLinkedFile: promisify(refreshLinkedFile),
  223. },
  224. }