TemplatesManager.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. const { Project } = require('../../models/Project')
  2. const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler')
  3. const ProjectOptionsHandler =
  4. require('../Project/ProjectOptionsHandler').promises
  5. const ProjectRootDocManager =
  6. require('../Project/ProjectRootDocManager').promises
  7. const ProjectUploadManager = require('../Uploads/ProjectUploadManager')
  8. const fs = require('fs')
  9. const util = require('util')
  10. const logger = require('@overleaf/logger')
  11. const {
  12. fetchJson,
  13. fetchStreamWithResponse,
  14. RequestFailedError,
  15. } = require('@overleaf/fetch-utils')
  16. const settings = require('@overleaf/settings')
  17. const crypto = require('crypto')
  18. const Errors = require('../Errors/Errors')
  19. const { pipeline } = require('stream/promises')
  20. const TemplatesManager = {
  21. async createProjectFromV1Template(
  22. brandVariationId,
  23. compiler,
  24. mainFile,
  25. templateId,
  26. templateName,
  27. templateVersionId,
  28. userId,
  29. imageName
  30. ) {
  31. const zipUrl = `${settings.apis.v1.url}/api/v1/overleaf/templates/${templateVersionId}`
  32. const zipReq = await fetchStreamWithResponse(zipUrl, {
  33. basicAuth: {
  34. user: settings.apis.v1.user,
  35. password: settings.apis.v1.pass,
  36. },
  37. signal: AbortSignal.timeout(settings.apis.v1.timeout),
  38. })
  39. const projectName = ProjectDetailsHandler.fixProjectName(templateName)
  40. const dumpPath = `${settings.path.dumpFolder}/${crypto.randomUUID()}`
  41. const writeStream = fs.createWriteStream(dumpPath)
  42. try {
  43. const attributes = {
  44. fromV1TemplateId: templateId,
  45. fromV1TemplateVersionId: templateVersionId,
  46. }
  47. await pipeline(zipReq.stream, writeStream)
  48. if (zipReq.response.status !== 200) {
  49. logger.warn(
  50. { uri: zipUrl, statusCode: zipReq.response.status },
  51. 'non-success code getting zip from template API'
  52. )
  53. throw new Error(`get zip failed: ${zipReq.response.status}`)
  54. }
  55. const project =
  56. await ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
  57. userId,
  58. projectName,
  59. dumpPath,
  60. attributes
  61. )
  62. await TemplatesManager._setCompiler(project._id, compiler)
  63. await TemplatesManager._setImage(project._id, imageName)
  64. await TemplatesManager._setMainFile(project._id, mainFile)
  65. await TemplatesManager._setBrandVariationId(project._id, brandVariationId)
  66. const update = {
  67. fromV1TemplateId: templateId,
  68. fromV1TemplateVersionId: templateVersionId,
  69. }
  70. await Project.updateOne({ _id: project._id }, update, {})
  71. return project
  72. } finally {
  73. await fs.promises.unlink(dumpPath)
  74. }
  75. },
  76. async _setCompiler(projectId, compiler) {
  77. if (compiler == null) {
  78. return
  79. }
  80. await ProjectOptionsHandler.setCompiler(projectId, compiler)
  81. },
  82. async _setImage(projectId, imageName) {
  83. if (!imageName) {
  84. imageName = 'wl_texlive:2018.1'
  85. }
  86. await ProjectOptionsHandler.setImageName(projectId, imageName)
  87. },
  88. async _setMainFile(projectId, mainFile) {
  89. if (mainFile == null) {
  90. return
  91. }
  92. await ProjectRootDocManager.setRootDocFromName(projectId, mainFile)
  93. },
  94. async _setBrandVariationId(projectId, brandVariationId) {
  95. if (brandVariationId == null) {
  96. return
  97. }
  98. await ProjectOptionsHandler.setBrandVariationId(projectId, brandVariationId)
  99. },
  100. async fetchFromV1(templateId) {
  101. const url = new URL(`/api/v2/templates/${templateId}`, settings.apis.v1.url)
  102. try {
  103. return await fetchJson(url, {
  104. basicAuth: {
  105. user: settings.apis.v1.user,
  106. password: settings.apis.v1.pass,
  107. },
  108. signal: AbortSignal.timeout(settings.apis.v1.timeout),
  109. })
  110. } catch (err) {
  111. if (err instanceof RequestFailedError && err.response.status === 404) {
  112. throw new Errors.NotFoundError()
  113. } else {
  114. throw err
  115. }
  116. }
  117. },
  118. }
  119. module.exports = {
  120. promises: TemplatesManager,
  121. createProjectFromV1Template: util.callbackify(
  122. TemplatesManager.createProjectFromV1Template
  123. ),
  124. fetchFromV1: util.callbackify(TemplatesManager.fetchFromV1),
  125. }