| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /* eslint-disable
- camelcase,
- n/handle-callback-err,
- max-len,
- */
- // TODO: This file was created by bulk-decaffeinate.
- // Fix any style issues and re-enable lint.
- /*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS103: Rewrite code to no longer use __guard__
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
- let CompileManager
- const Settings = require('@overleaf/settings')
- const RedisWrapper = require('../../infrastructure/RedisWrapper')
- const rclient = RedisWrapper.client('clsi_recently_compiled')
- const ProjectGetter = require('../Project/ProjectGetter')
- const ProjectRootDocManager = require('../Project/ProjectRootDocManager')
- const UserGetter = require('../User/UserGetter')
- const ClsiManager = require('./ClsiManager')
- const Metrics = require('@overleaf/metrics')
- const rateLimiter = require('../../infrastructure/RateLimiter')
- module.exports = CompileManager = {
- compile(project_id, user_id, options, _callback) {
- if (options == null) {
- options = {}
- }
- if (_callback == null) {
- _callback = function () {}
- }
- const timer = new Metrics.Timer('editor.compile')
- const callback = function (...args) {
- timer.done()
- return _callback(...Array.from(args || []))
- }
- return CompileManager._checkIfRecentlyCompiled(
- project_id,
- user_id,
- function (error, recentlyCompiled) {
- if (error != null) {
- return callback(error)
- }
- if (recentlyCompiled) {
- return callback(null, 'too-recently-compiled', [])
- }
- return CompileManager._checkIfAutoCompileLimitHasBeenHit(
- options.isAutoCompile,
- 'everyone',
- function (err, canCompile) {
- if (!canCompile) {
- return callback(null, 'autocompile-backoff', [])
- }
- return ProjectRootDocManager.ensureRootDocumentIsSet(
- project_id,
- function (error) {
- if (error != null) {
- return callback(error)
- }
- return CompileManager.getProjectCompileLimits(
- project_id,
- function (error, limits) {
- if (error != null) {
- return callback(error)
- }
- for (const key in limits) {
- const value = limits[key]
- options[key] = value
- }
- // Put a lower limit on autocompiles for free users, based on compileGroup
- return CompileManager._checkCompileGroupAutoCompileLimit(
- options.isAutoCompile,
- limits.compileGroup,
- function (err, canCompile) {
- if (!canCompile) {
- return callback(null, 'autocompile-backoff', [])
- }
- // only pass user_id down to clsi if this is a per-user compile
- const compileAsUser = Settings.disablePerUserCompiles
- ? undefined
- : user_id
- return ClsiManager.sendRequest(
- project_id,
- compileAsUser,
- options,
- function (
- error,
- status,
- outputFiles,
- clsiServerId,
- validationProblems,
- stats,
- timings
- ) {
- if (error != null) {
- return callback(error)
- }
- return callback(
- null,
- status,
- outputFiles,
- clsiServerId,
- limits,
- validationProblems,
- stats,
- timings
- )
- }
- )
- }
- )
- }
- )
- }
- )
- }
- )
- }
- )
- },
- stopCompile(project_id, user_id, callback) {
- if (callback == null) {
- callback = function () {}
- }
- return CompileManager.getProjectCompileLimits(
- project_id,
- function (error, limits) {
- if (error != null) {
- return callback(error)
- }
- return ClsiManager.stopCompile(project_id, user_id, limits, callback)
- }
- )
- },
- deleteAuxFiles(project_id, user_id, clsiserverid, callback) {
- if (callback == null) {
- callback = function () {}
- }
- return CompileManager.getProjectCompileLimits(
- project_id,
- function (error, limits) {
- if (error != null) {
- return callback(error)
- }
- ClsiManager.deleteAuxFiles(
- project_id,
- user_id,
- limits,
- clsiserverid,
- callback
- )
- }
- )
- },
- getProjectCompileLimits(project_id, callback) {
- if (callback == null) {
- callback = function () {}
- }
- return ProjectGetter.getProject(
- project_id,
- { owner_ref: 1 },
- function (error, project) {
- if (error != null) {
- return callback(error)
- }
- return UserGetter.getUser(
- project.owner_ref,
- { alphaProgram: 1, betaProgram: 1, features: 1 },
- function (err, owner) {
- if (error != null) {
- return callback(error)
- }
- const ownerFeatures = (owner && owner.features) || {}
- // put alpha users into their own compile group
- if (owner && owner.alphaProgram) {
- ownerFeatures.compileGroup = 'alpha'
- }
- return callback(null, {
- timeout:
- ownerFeatures.compileTimeout ||
- Settings.defaultFeatures.compileTimeout,
- compileGroup:
- ownerFeatures.compileGroup ||
- Settings.defaultFeatures.compileGroup,
- })
- }
- )
- }
- )
- },
- COMPILE_DELAY: 1, // seconds
- _checkIfRecentlyCompiled(project_id, user_id, callback) {
- if (callback == null) {
- callback = function () {}
- }
- const key = `compile:${project_id}:${user_id}`
- return rclient.set(
- key,
- true,
- 'EX',
- this.COMPILE_DELAY,
- 'NX',
- function (error, ok) {
- if (error != null) {
- return callback(error)
- }
- if (ok === 'OK') {
- return callback(null, false)
- } else {
- return callback(null, true)
- }
- }
- )
- },
- _checkCompileGroupAutoCompileLimit(isAutoCompile, compileGroup, callback) {
- if (callback == null) {
- callback = function () {}
- }
- if (!isAutoCompile) {
- return callback(null, true)
- }
- if (compileGroup === 'standard') {
- // apply extra limits to the standard compile group
- return CompileManager._checkIfAutoCompileLimitHasBeenHit(
- isAutoCompile,
- compileGroup,
- callback
- )
- } else {
- Metrics.inc(`auto-compile-${compileGroup}`)
- return callback(null, true)
- }
- }, // always allow priority group users to compile
- _checkIfAutoCompileLimitHasBeenHit(isAutoCompile, compileGroup, callback) {
- if (callback == null) {
- callback = function () {}
- }
- if (!isAutoCompile) {
- return callback(null, true)
- }
- Metrics.inc(`auto-compile-${compileGroup}`)
- const opts = {
- endpointName: 'auto_compile',
- timeInterval: 20,
- subjectName: compileGroup,
- throttle: Settings.rateLimit.autoCompile[compileGroup] || 25,
- }
- return rateLimiter.addCount(opts, function (err, canCompile) {
- if (err != null) {
- canCompile = false
- }
- if (!canCompile) {
- Metrics.inc(`auto-compile-${compileGroup}-limited`)
- }
- return callback(err, canCompile)
- })
- },
- wordCount(project_id, user_id, file, clsiserverid, callback) {
- if (callback == null) {
- callback = function () {}
- }
- return CompileManager.getProjectCompileLimits(
- project_id,
- function (error, limits) {
- if (error != null) {
- return callback(error)
- }
- ClsiManager.wordCount(
- project_id,
- user_id,
- file,
- limits,
- clsiserverid,
- callback
- )
- }
- )
- },
- }
|