overleaf/services/clsi/app/js/OutputFileArchiveManager.js
Andrew Rumble 92f62f91c1 Merge pull request #18148 from overleaf/ar-add-output-zip-endpoint-to-clsi
[clsi] Add endpoints to get zip of output files

GitOrigin-RevId: a1a935e8170ab5a8d40baa6d96f8e42fe22c2e8c
2024-05-02 08:03:44 +00:00

90 lines
2.4 KiB
JavaScript

let OutputFileArchiveManager
const archiver = require('archiver')
const OutputCacheManager = require('./OutputCacheManager')
const OutputFileFinder = require('./OutputFileFinder')
const Settings = require('@overleaf/settings')
const { open } = require('node:fs/promises')
const path = require('node:path')
const { NotFoundError } = require('./Errors')
function getContentDir(projectId, userId) {
let subDir
if (userId != null) {
subDir = `${projectId}-${userId}`
} else {
subDir = projectId
}
return `${Settings.path.outputDir}/${subDir}/`
}
module.exports = OutputFileArchiveManager = {
async archiveFilesForBuild(projectId, userId, build, files = []) {
const contentDir = getContentDir(projectId, userId)
const validFiles = await (files.length > 0
? this._getRequestedOutputFiles(projectId, userId, build, files)
: this._getAllOutputFiles(projectId, userId, build))
const archive = archiver('zip')
const missingFiles = files.filter(
file => !validFiles.includes(path.basename(file))
)
for (const file of validFiles) {
try {
const fileHandle = await open(
`${contentDir}${OutputCacheManager.path(build, file)}`
)
const fileStream = fileHandle.createReadStream()
archive.append(fileStream, { name: file })
} catch (error) {
missingFiles.push(file)
}
}
if (missingFiles.length > 0) {
archive.append(missingFiles.join('\n'), {
name: 'missing_files.txt',
})
}
await archive.finalize()
return archive
},
async _getAllOutputFiles(projectId, userId, build) {
const contentDir = getContentDir(projectId, userId)
try {
const { outputFiles } = await OutputFileFinder.promises.findOutputFiles(
[],
`${contentDir}${OutputCacheManager.path(build, '.')}`
)
return outputFiles.map(({ path }) => path)
} catch (error) {
if (
error.code === 'ENOENT' ||
error.code === 'ENOTDIR' ||
error.code === 'EACCES'
) {
throw new NotFoundError('Output files not found')
}
throw error
}
},
async _getRequestedOutputFiles(projectId, userId, build, files) {
const outputFiles = new Set(
await OutputFileArchiveManager._getAllOutputFiles(
projectId,
userId,
build
)
)
return files.filter(file => outputFiles.has(file))
},
}