settings.defaults.cjs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. const Path = require('node:path')
  2. const os = require('node:os')
  3. const fs = require('node:fs')
  4. const isPreEmptible = process.env.PREEMPTIBLE === 'TRUE'
  5. const CLSI_SERVER_ID = os.hostname().replace('-ctr', '')
  6. module.exports = {
  7. compileSizeLimit: process.env.COMPILE_SIZE_LIMIT || '7mb',
  8. processLifespanLimitMs:
  9. parseInt(process.env.PROCESS_LIFE_SPAN_LIMIT_MS) || 60 * 60 * 24 * 1000 * 2,
  10. catchErrors: process.env.CATCH_ERRORS === 'true',
  11. path: {
  12. compilesDir:
  13. process.env.CLSI_COMPILES_PATH || Path.resolve(__dirname, '../compiles'),
  14. outputDir:
  15. process.env.CLSI_OUTPUT_PATH || Path.resolve(__dirname, '../output'),
  16. clsiCacheDir:
  17. process.env.CLSI_CACHE_PATH || Path.resolve(__dirname, '../cache'),
  18. synctexBaseDir(projectId) {
  19. return Path.join(this.compilesDir, projectId)
  20. },
  21. },
  22. internal: {
  23. clsi: {
  24. port: 3013,
  25. host: process.env.LISTEN_ADDRESS || '127.0.0.1',
  26. },
  27. load_balancer_agent: {
  28. report_load: process.env.LOAD_BALANCER_AGENT_REPORT_LOAD !== 'false',
  29. load_port: 3048,
  30. local_port: 3049,
  31. allow_maintenance:
  32. (
  33. process.env.LOAD_BALANCER_AGENT_ALLOW_MAINTENANCE ?? ''
  34. ).toLowerCase() !== 'false',
  35. },
  36. },
  37. apis: {
  38. clsi: {
  39. // Internal requests (used by tests only at the time of writing).
  40. url: `http://${process.env.CLSI_HOST || '127.0.0.1'}:3013`,
  41. // External url prefix for output files, e.g. for requests via load-balancers.
  42. outputUrlPrefix: `${process.env.ZONE ? `/zone/${process.env.ZONE}` : ''}`,
  43. clsiServerId: process.env.CLSI_SERVER_ID || CLSI_SERVER_ID,
  44. downloadHost: process.env.DOWNLOAD_HOST || 'http://localhost:8080',
  45. },
  46. clsiPerf: {
  47. host: `${process.env.CLSI_PERF_HOST || '127.0.0.1'}:${
  48. process.env.CLSI_PERF_PORT || '3043'
  49. }`,
  50. },
  51. clsiCache: {
  52. enabled: !!process.env.CLSI_CACHE_INSTANCES,
  53. shards: JSON.parse(process.env.CLSI_CACHE_INSTANCES || '[]').filter(
  54. ({ zone, readOnly }) => zone === process.env.ZONE && !readOnly
  55. ),
  56. },
  57. },
  58. smokeTest: process.env.SMOKE_TEST || false,
  59. project_cache_length_ms: 1000 * 60 * 60 * 24,
  60. parallelFileDownloads: process.env.FILESTORE_PARALLEL_FILE_DOWNLOADS || 1,
  61. filestoreDomainOveride: process.env.FILESTORE_DOMAIN_OVERRIDE,
  62. texliveImageNameOveride: process.env.TEX_LIVE_IMAGE_NAME_OVERRIDE,
  63. texliveOpenoutAny: process.env.TEXLIVE_OPENOUT_ANY,
  64. texliveMaxPrintLine: process.env.TEXLIVE_MAX_PRINT_LINE,
  65. enablePdfCaching: process.env.ENABLE_PDF_CACHING === 'true',
  66. enablePdfCachingDark: process.env.ENABLE_PDF_CACHING_DARK === 'true',
  67. pdfCachingMinChunkSize:
  68. parseInt(process.env.PDF_CACHING_MIN_CHUNK_SIZE, 10) || 1024,
  69. pdfCachingMaxProcessingTime:
  70. parseInt(process.env.PDF_CACHING_MAX_PROCESSING_TIME, 10) || 10 * 1000,
  71. pdfCachingEnableWorkerPool:
  72. process.env.PDF_CACHING_ENABLE_WORKER_POOL === 'true',
  73. pdfCachingWorkerPoolSize:
  74. parseInt(process.env.PDF_CACHING_WORKER_POOL_SIZE, 10) || 4,
  75. pdfCachingWorkerPoolBackLogLimit:
  76. parseInt(process.env.PDF_CACHING_WORKER_POOL_BACK_LOG_LIMIT, 10) || 40,
  77. compileConcurrencyLimit: isPreEmptible ? 32 : 64,
  78. performanceLogSamplingPercentage:
  79. parseFloat(process.env.CLSI_PERFORMANCE_LOG_SAMPLING, 10) || 0,
  80. }
  81. if (process.env.ALLOWED_COMPILE_GROUPS) {
  82. try {
  83. module.exports.allowedCompileGroups =
  84. process.env.ALLOWED_COMPILE_GROUPS.split(' ')
  85. } catch (error) {
  86. console.error(error, 'could not apply allowed compile group setting')
  87. process.exit(1)
  88. }
  89. }
  90. if ((process.env.DOCKER_RUNNER || process.env.SANDBOXED_COMPILES) === 'true') {
  91. if (
  92. !fs.existsSync(Path.join(__dirname, '..', 'app', 'js', 'DockerRunner.js'))
  93. ) {
  94. console.error(
  95. 'Sandboxed compiles are only available with Overleaf Server Pro. Compare Server Pro with Community Edition here: https://docs.overleaf.com/on-premises/welcome/server-pro-vs.-community-edition'
  96. )
  97. process.exit(1)
  98. }
  99. module.exports.clsi = {
  100. dockerRunner: true,
  101. docker: {
  102. runtime: process.env.DOCKER_RUNTIME,
  103. image:
  104. process.env.TEXLIVE_IMAGE ||
  105. process.env.TEX_LIVE_DOCKER_IMAGE ||
  106. 'quay.io/sharelatex/texlive-full:2017.1',
  107. env: {
  108. HOME: '/tmp',
  109. CLSI: 1,
  110. },
  111. socketPath: '/var/run/docker.sock',
  112. user: process.env.TEXLIVE_IMAGE_USER || 'tex',
  113. },
  114. optimiseInDocker: true,
  115. expireProjectAfterIdleMs: 24 * 60 * 60 * 1000,
  116. checkProjectsIntervalMs: 10 * 60 * 1000,
  117. }
  118. try {
  119. // Override individual docker settings using path-based keys, e.g.:
  120. // compileGroupDockerConfigs = {
  121. // priority: { 'HostConfig.CpuShares': 100 }
  122. // beta: { 'dotted.path.here', 'value'}
  123. // }
  124. const compileGroupConfig = JSON.parse(
  125. process.env.COMPILE_GROUP_DOCKER_CONFIGS || '{}'
  126. )
  127. // Automatically clean up wordcount and synctex containers
  128. const defaultCompileGroupConfig = {
  129. wordcount: { 'HostConfig.AutoRemove': true },
  130. synctex: { 'HostConfig.AutoRemove': true },
  131. 'synctex-output': { 'HostConfig.AutoRemove': true },
  132. }
  133. module.exports.clsi.docker.compileGroupConfig = Object.assign(
  134. defaultCompileGroupConfig,
  135. compileGroupConfig
  136. )
  137. } catch (error) {
  138. console.error(error, 'could not apply compile group docker configs')
  139. process.exit(1)
  140. }
  141. let seccompProfilePath
  142. try {
  143. seccompProfilePath = Path.resolve(__dirname, '../seccomp/clsi-profile.json')
  144. module.exports.clsi.docker.seccomp_profile =
  145. process.env.SECCOMP_PROFILE ||
  146. JSON.stringify(
  147. JSON.parse(require('node:fs').readFileSync(seccompProfilePath))
  148. )
  149. } catch (error) {
  150. console.error(
  151. error,
  152. `could not load seccomp profile from ${seccompProfilePath}`
  153. )
  154. process.exit(1)
  155. }
  156. if (process.env.APPARMOR_PROFILE) {
  157. try {
  158. module.exports.clsi.docker.apparmor_profile = process.env.APPARMOR_PROFILE
  159. } catch (error) {
  160. console.error(error, 'could not apply apparmor profile setting')
  161. process.exit(1)
  162. }
  163. }
  164. if (process.env.ALLOWED_IMAGES) {
  165. try {
  166. module.exports.clsi.docker.allowedImages =
  167. process.env.ALLOWED_IMAGES.split(' ')
  168. } catch (error) {
  169. console.error(error, 'could not apply allowed images setting')
  170. process.exit(1)
  171. }
  172. }
  173. module.exports.path.synctexBaseDir = () => '/compile'
  174. module.exports.path.sandboxedCompilesHostDirCompiles =
  175. process.env.SANDBOXED_COMPILES_HOST_DIR_COMPILES ||
  176. process.env.SANDBOXED_COMPILES_HOST_DIR ||
  177. process.env.COMPILES_HOST_DIR
  178. if (!module.exports.path.sandboxedCompilesHostDirCompiles) {
  179. throw new Error(
  180. 'SANDBOXED_COMPILES enabled, but SANDBOXED_COMPILES_HOST_DIR_COMPILES not set'
  181. )
  182. }
  183. module.exports.path.sandboxedCompilesHostDirOutput =
  184. process.env.SANDBOXED_COMPILES_HOST_DIR_OUTPUT ||
  185. process.env.OUTPUT_HOST_DIR
  186. if (!module.exports.path.sandboxedCompilesHostDirOutput) {
  187. // TODO(das7pad): Enforce in a future major version of Server Pro.
  188. // throw new Error(
  189. // 'SANDBOXED_COMPILES enabled, but SANDBOXED_COMPILES_HOST_DIR_OUTPUT not set'
  190. // )
  191. }
  192. }