OutputFileArchiveManager.js 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. const archiver = require('archiver')
  2. const OutputCacheManager = require('./OutputCacheManager')
  3. const OutputFileFinder = require('./OutputFileFinder')
  4. const Settings = require('@overleaf/settings')
  5. const { open } = require('node:fs/promises')
  6. const path = require('path')
  7. const { NotFoundError } = require('./Errors')
  8. // NOTE: Updating this list requires a corresponding change in
  9. // * services/web/frontend/js/features/pdf-preview/util/file-list.js
  10. const ignoreFiles = ['output.fls', 'output.fdb_latexmk']
  11. function getContentDir(projectId, userId) {
  12. let subDir
  13. if (userId != null) {
  14. subDir = `${projectId}-${userId}`
  15. } else {
  16. subDir = projectId
  17. }
  18. return `${Settings.path.outputDir}/${subDir}/`
  19. }
  20. module.exports = {
  21. async archiveFilesForBuild(projectId, userId, build) {
  22. const validFiles = await this._getAllOutputFiles(projectId, userId, build)
  23. const archive = archiver('zip')
  24. const missingFiles = []
  25. for (const file of validFiles) {
  26. try {
  27. const fileHandle = await open(file, 'r')
  28. const fileStream = fileHandle.createReadStream()
  29. archive.append(fileStream, { name: path.basename(file) })
  30. } catch (error) {
  31. missingFiles.push(file)
  32. }
  33. }
  34. if (missingFiles.length > 0) {
  35. archive.append(missingFiles.join('\n'), {
  36. name: 'missing_files.txt',
  37. })
  38. }
  39. await archive.finalize()
  40. return archive
  41. },
  42. async _getAllOutputFiles(projectId, userId, build) {
  43. const contentDir = getContentDir(projectId, userId)
  44. try {
  45. const { outputFiles } = await OutputFileFinder.promises.findOutputFiles(
  46. [],
  47. `${contentDir}${OutputCacheManager.path(build, '.')}`
  48. )
  49. return outputFiles
  50. .filter(
  51. // Ignore the pdf and also ignore the files ignored by the frontend.
  52. ({ path }) => path !== 'output.pdf' && !ignoreFiles.includes(path)
  53. )
  54. .map(
  55. ({ path }) => `${contentDir}${OutputCacheManager.path(build, path)}`
  56. )
  57. } catch (error) {
  58. if (
  59. error.code === 'ENOENT' ||
  60. error.code === 'ENOTDIR' ||
  61. error.code === 'EACCES'
  62. ) {
  63. throw new NotFoundError('Output files not found')
  64. }
  65. throw error
  66. }
  67. },
  68. }