OutputFileArchiveManager.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. let OutputFileArchiveManager
  2. const archiver = require('archiver')
  3. const OutputCacheManager = require('./OutputCacheManager')
  4. const OutputFileFinder = require('./OutputFileFinder')
  5. const Settings = require('@overleaf/settings')
  6. const { open } = require('node:fs/promises')
  7. const path = require('node:path')
  8. const { NotFoundError } = require('./Errors')
  9. function getContentDir(projectId, userId) {
  10. let subDir
  11. if (userId != null) {
  12. subDir = `${projectId}-${userId}`
  13. } else {
  14. subDir = projectId
  15. }
  16. return `${Settings.path.outputDir}/${subDir}/`
  17. }
  18. module.exports = OutputFileArchiveManager = {
  19. async archiveFilesForBuild(projectId, userId, build, files = []) {
  20. const contentDir = getContentDir(projectId, userId)
  21. const validFiles = await (files.length > 0
  22. ? this._getRequestedOutputFiles(projectId, userId, build, files)
  23. : this._getAllOutputFiles(projectId, userId, build))
  24. const archive = archiver('zip')
  25. const missingFiles = files.filter(
  26. file => !validFiles.includes(path.basename(file))
  27. )
  28. for (const file of validFiles) {
  29. try {
  30. const fileHandle = await open(
  31. `${contentDir}${OutputCacheManager.path(build, file)}`
  32. )
  33. const fileStream = fileHandle.createReadStream()
  34. archive.append(fileStream, { name: file })
  35. } catch (error) {
  36. missingFiles.push(file)
  37. }
  38. }
  39. if (missingFiles.length > 0) {
  40. archive.append(missingFiles.join('\n'), {
  41. name: 'missing_files.txt',
  42. })
  43. }
  44. await archive.finalize()
  45. return archive
  46. },
  47. async _getAllOutputFiles(projectId, userId, build) {
  48. const contentDir = getContentDir(projectId, userId)
  49. try {
  50. const { outputFiles } = await OutputFileFinder.promises.findOutputFiles(
  51. [],
  52. `${contentDir}${OutputCacheManager.path(build, '.')}`
  53. )
  54. return outputFiles.map(({ path }) => path)
  55. } catch (error) {
  56. if (
  57. error.code === 'ENOENT' ||
  58. error.code === 'ENOTDIR' ||
  59. error.code === 'EACCES'
  60. ) {
  61. throw new NotFoundError('Output files not found')
  62. }
  63. throw error
  64. }
  65. },
  66. async _getRequestedOutputFiles(projectId, userId, build, files) {
  67. const outputFiles = new Set(
  68. await OutputFileArchiveManager._getAllOutputFiles(
  69. projectId,
  70. userId,
  71. build
  72. )
  73. )
  74. return files.filter(file => outputFiles.has(file))
  75. },
  76. }