ProjectOptionsHandler.mjs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { Project } from '../../models/Project.mjs'
  2. import settings from '@overleaf/settings'
  3. import { callbackify } from 'node:util'
  4. import { db, ObjectId } from '../../infrastructure/mongodb.js'
  5. import Errors from '../Errors/Errors.js'
  6. import mongodb from 'mongodb-legacy'
  7. const safeCompilers = ['xelatex', 'pdflatex', 'latex', 'lualatex']
  8. const { ReturnDocument } = mongodb
  9. const ProjectOptionsHandler = {
  10. async setCompiler(projectId, compiler) {
  11. if (!compiler) {
  12. return
  13. }
  14. compiler = compiler.toLowerCase()
  15. if (!safeCompilers.includes(compiler)) {
  16. throw new Error(`invalid compiler: ${compiler}`)
  17. }
  18. const conditions = { _id: projectId }
  19. const update = { compiler }
  20. return Project.updateOne(conditions, update, {})
  21. },
  22. async setImageName(projectId, imageName) {
  23. if (!imageName || !Array.isArray(settings.allowedImageNames)) {
  24. return
  25. }
  26. imageName = imageName.toLowerCase()
  27. const isAllowed = settings.allowedImageNames.find(
  28. allowed => imageName === allowed.imageName
  29. )
  30. if (!isAllowed) {
  31. throw new Error(`invalid imageName: ${imageName}`)
  32. }
  33. const conditions = { _id: projectId }
  34. const update = { imageName: settings.imageRoot + '/' + imageName }
  35. return Project.updateOne(conditions, update, {})
  36. },
  37. async setSpellCheckLanguage(projectId, languageCode) {
  38. if (!Array.isArray(settings.languages)) {
  39. return
  40. }
  41. const language = settings.languages.find(
  42. language => language.code === languageCode
  43. )
  44. if (languageCode && !language) {
  45. throw new Error(`invalid languageCode: ${languageCode}`)
  46. }
  47. const conditions = { _id: projectId }
  48. const update = { spellCheckLanguage: languageCode }
  49. return Project.updateOne(conditions, update, {})
  50. },
  51. async setBrandVariationId(projectId, brandVariationId) {
  52. if (!brandVariationId) {
  53. return
  54. }
  55. const conditions = { _id: projectId }
  56. const update = { brandVariationId }
  57. return Project.updateOne(conditions, update, {})
  58. },
  59. async unsetBrandVariationId(projectId) {
  60. const conditions = { _id: projectId }
  61. const update = { $unset: { brandVariationId: 1 } }
  62. return Project.updateOne(conditions, update, {})
  63. },
  64. async setHistoryRangesSupport(projectId, enabled) {
  65. const conditions = { _id: new ObjectId(projectId) }
  66. const update = {
  67. $set: { 'overleaf.history.rangesSupportEnabled': enabled },
  68. }
  69. // NOTE: Updating the Mongoose model with the same query doesn't work. Maybe
  70. // because rangesSupportEnabled is not part of the schema?
  71. return db.projects.updateOne(conditions, update)
  72. },
  73. async setOTMigrationStage(projectId, nextStage) {
  74. const project = await db.projects.findOneAndUpdate(
  75. { _id: new ObjectId(projectId) },
  76. // Use $max to ensure that we never downgrade the migration stage.
  77. { $max: { 'overleaf.history.otMigrationStage': nextStage } },
  78. {
  79. returnDocument: ReturnDocument.AFTER,
  80. projection: { 'overleaf.history.otMigrationStage': 1 },
  81. }
  82. )
  83. if (!project) throw new Errors.NotFoundError('project does not exist')
  84. const { otMigrationStage } = project.overleaf.history
  85. return { otMigrationStage }
  86. },
  87. }
  88. export default {
  89. setCompiler: callbackify(ProjectOptionsHandler.setCompiler),
  90. setImageName: callbackify(ProjectOptionsHandler.setImageName),
  91. setSpellCheckLanguage: callbackify(
  92. ProjectOptionsHandler.setSpellCheckLanguage
  93. ),
  94. setBrandVariationId: callbackify(ProjectOptionsHandler.setBrandVariationId),
  95. unsetBrandVariationId: callbackify(
  96. ProjectOptionsHandler.unsetBrandVariationId
  97. ),
  98. setHistoryRangesSupport: callbackify(
  99. ProjectOptionsHandler.setHistoryRangesSupport
  100. ),
  101. promises: ProjectOptionsHandler,
  102. }