ProjectOutputFileAgent.mjs 5.7 KB

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