[misc] run format:fix

This commit is contained in:
Jakob Ackermann 2021-07-07 12:43:33 +01:00
parent d812c86c51
commit d7c641eaf7
3 changed files with 993 additions and 839 deletions

View file

@ -16,42 +16,36 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
const coffee = require("coffee-script"); const coffee = require('coffee-script')
const fs = require("fs"); const fs = require('fs')
const { const { spawn } = require('child_process')
spawn const { exec } = require('child_process')
} = require("child_process"); const rimraf = require('rimraf')
const { const Path = require('path')
exec const semver = require('semver')
} = require("child_process"); const knox = require('knox')
const rimraf = require("rimraf"); const crypto = require('crypto')
const Path = require("path"); const async = require('async')
const semver = require("semver"); const settings = require('settings-sharelatex')
const knox = require("knox"); const _ = require('underscore')
const crypto = require("crypto");
const async = require("async");
const settings = require("settings-sharelatex");
const _ = require("underscore");
const SERVICES = require('./config/services')
const SERVICES = require("./config/services"); module.exports = function (grunt) {
let Helpers
let service
grunt.loadNpmTasks('grunt-bunyan')
grunt.loadNpmTasks('grunt-execute')
grunt.loadNpmTasks('grunt-available-tasks')
grunt.loadNpmTasks('grunt-concurrent')
grunt.loadNpmTasks('grunt-contrib-coffee')
grunt.loadNpmTasks('grunt-shell')
module.exports = function(grunt) { grunt.task.loadTasks('./tasks')
let Helpers;
let service;
grunt.loadNpmTasks('grunt-bunyan');
grunt.loadNpmTasks('grunt-execute');
grunt.loadNpmTasks('grunt-available-tasks');
grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks("grunt-contrib-coffee");
grunt.loadNpmTasks("grunt-shell");
grunt.task.loadTasks("./tasks"); const execute = {}
const execute = {};
for (service of Array.from(SERVICES)) { for (service of Array.from(SERVICES)) {
execute[service.name] = execute[service.name] = { src: `${service.name}/app.js` }
{src: `${service.name}/app.js`};
} }
grunt.initConfig({ grunt.initConfig({
@ -59,178 +53,221 @@ module.exports = function(grunt) {
concurrent: { concurrent: {
all: { all: {
tasks: (((() => { tasks: (() => {
const result = []; const result = []
for (service of Array.from(SERVICES)) { result.push(`run:${service.name}`); for (service of Array.from(SERVICES)) {
result.push(`run:${service.name}`)
} }
return result; return result
})())), })(),
options: { options: {
limit: SERVICES.length, limit: SERVICES.length,
logConcurrentOutput: true logConcurrentOutput: true,
},
},
},
availabletasks: { availabletasks: {
tasks: { tasks: {
options: { options: {
filter: 'exclude', filter: 'exclude',
tasks: [ tasks: ['concurrent', 'execute', 'bunyan', 'availabletasks'],
'concurrent',
'execute',
'bunyan',
'availabletasks'
],
groups: { groups: {
"Run tasks": [ 'Run tasks': ['run', 'run:all', 'default'].concat(
"run", (() => {
"run:all", const result1 = []
"default" for (service of Array.from(SERVICES)) {
].concat(((() => { result1.push(`run:${service.name}`)
const result1 = [];
for (service of Array.from(SERVICES)) { result1.push(`run:${service.name}`);
} }
return result1; return result1
})())), })()
"Misc": [ ),
"help" Misc: ['help'],
'Install tasks': (() => {
const result2 = []
for (service of Array.from(SERVICES)) {
result2.push(`install:${service.name}`)
}
return result2
})().concat(['install:all', 'install']),
'Update tasks': (() => {
const result3 = []
for (service of Array.from(SERVICES)) {
result3.push(`update:${service.name}`)
}
return result3
})().concat(['update:all', 'update']),
Checks: [
'check',
'check:redis',
'check:latexmk',
'check:s3',
'check:make',
'check:mongo',
], ],
"Install tasks": ((() => { },
const result2 = []; },
for (service of Array.from(SERVICES)) { result2.push(`install:${service.name}`); },
} },
return result2; })
})()).concat(["install:all", "install"]),
"Update tasks": ((() => {
const result3 = [];
for (service of Array.from(SERVICES)) { result3.push(`update:${service.name}`);
}
return result3;
})()).concat(["update:all", "update"]),
"Checks": ["check", "check:redis", "check:latexmk", "check:s3", "check:make", "check:mongo"]
}
}
}
}});
for (service of Array.from(SERVICES)) { for (service of Array.from(SERVICES)) {
((service => grunt.registerTask(`install:${service.name}`, `Download and set up the ${service.name} service`, function() { ;(service =>
const done = this.async(); grunt.registerTask(
return Helpers.installService(service, done); `install:${service.name}`,
})))(service); `Download and set up the ${service.name} service`,
function () {
const done = this.async()
return Helpers.installService(service, done)
}
))(service)
} }
grunt.registerTask(
'install:all',
grunt.registerTask('install:all', "Download and set up all ShareLaTeX services", 'Download and set up all ShareLaTeX services',
[].concat( []
((() => { .concat(
const result4 = []; (() => {
for (service of Array.from(SERVICES)) { result4.push(`install:${service.name}`); const result4 = []
for (service of Array.from(SERVICES)) {
result4.push(`install:${service.name}`)
} }
return result4; return result4
})()) })()
).concat(['postinstall']) )
); .concat(['postinstall'])
grunt.registerTask('install', 'install:all');
grunt.registerTask('postinstall', 'Explain postinstall steps', function() {
return Helpers.postinstallMessage(this.async());
});
grunt.registerTask('update:all', "Checkout and update all ShareLaTeX services",
["check:make"].concat(
((() => {
const result5 = [];
for (service of Array.from(SERVICES)) { result5.push(`update:${service.name}`);
}
return result5;
})())
) )
);
grunt.registerTask('update', 'update:all');
grunt.registerTask('run', "Run all of the sharelatex processes", ['concurrent:all']);
grunt.registerTask('run:all', 'run');
grunt.registerTask('help', 'Display this help list', 'availabletasks'); grunt.registerTask('install', 'install:all')
grunt.registerTask('default', 'run'); grunt.registerTask('postinstall', 'Explain postinstall steps', function () {
return Helpers.postinstallMessage(this.async())
})
grunt.registerTask("check:redis", "Check that redis is installed and running", function() { grunt.registerTask(
return Helpers.checkRedisConnect(this.async()); 'update:all',
}); 'Checkout and update all ShareLaTeX services',
['check:make'].concat(
grunt.registerTask("check:mongo", "Check that mongo is installed", function() { (() => {
return Helpers.checkMongoConnect(this.async()); const result5 = []
}); for (service of Array.from(SERVICES)) {
result5.push(`update:${service.name}`)
grunt.registerTask("check", "Check that you have the required dependencies installed", ["check:redis", "check:mongo", "check:make"]);
grunt.registerTask("check:make", "Check that make is installed", function() {
return Helpers.checkMake(this.async());
});
return Helpers = {
installService(service, callback) {
if (callback == null) { callback = function(error) {}; }
console.log(`Installing ${service.name}`);
return Helpers.cloneGitRepo(service, function(error) {
if (error != null) {
return callback(error);
} else {
return callback();
} }
}); return result5
})()
)
)
grunt.registerTask('update', 'update:all')
grunt.registerTask('run', 'Run all of the sharelatex processes', [
'concurrent:all',
])
grunt.registerTask('run:all', 'run')
grunt.registerTask('help', 'Display this help list', 'availabletasks')
grunt.registerTask('default', 'run')
grunt.registerTask(
'check:redis',
'Check that redis is installed and running',
function () {
return Helpers.checkRedisConnect(this.async())
}
)
grunt.registerTask(
'check:mongo',
'Check that mongo is installed',
function () {
return Helpers.checkMongoConnect(this.async())
}
)
grunt.registerTask(
'check',
'Check that you have the required dependencies installed',
['check:redis', 'check:mongo', 'check:make']
)
grunt.registerTask('check:make', 'Check that make is installed', function () {
return Helpers.checkMake(this.async())
})
return (Helpers = {
installService(service, callback) {
if (callback == null) {
callback = function (error) {}
}
console.log(`Installing ${service.name}`)
return Helpers.cloneGitRepo(service, function (error) {
if (error != null) {
return callback(error)
} else {
return callback()
}
})
}, },
cloneGitRepo(service, callback) { cloneGitRepo(service, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
const repo_src = service.repo; callback = function (error) {}
const dir = service.name; }
const repo_src = service.repo
const dir = service.name
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
const proc = spawn("git", [ const proc = spawn('git', ['clone', repo_src, dir], {
"clone", stdio: 'inherit',
repo_src, })
dir return proc.on('close', () =>
], {stdio: "inherit"}); Helpers.checkoutVersion(service, callback)
return proc.on("close", () => Helpers.checkoutVersion(service, callback)); )
} else { } else {
console.log(`${dir} already installed, skipping.`); console.log(`${dir} already installed, skipping.`)
return callback(); return callback()
} }
}, },
checkoutVersion(service, callback) { checkoutVersion(service, callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
const dir = service.name; callback = function (error) {}
grunt.log.write(`checking out ${service.name} ${service.version}`); }
const proc = spawn("git", ["checkout", service.version], {stdio: "inherit", cwd: dir}); const dir = service.name
return proc.on("close", () => callback()); grunt.log.write(`checking out ${service.name} ${service.version}`)
const proc = spawn('git', ['checkout', service.version], {
stdio: 'inherit',
cwd: dir,
})
return proc.on('close', () => callback())
}, },
postinstallMessage(callback) { postinstallMessage(callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
callback = function (error) {}
}
grunt.log.write(`\ grunt.log.write(`\
Services cloned: Services cloned:
${(() => { ${(() => {
const result6 = []; const result6 = []
for (service of Array.from(SERVICES)) { result6.push(service.name); for (service of Array.from(SERVICES)) {
result6.push(service.name)
} }
return result6; return result6
})()} })()}
To install services run: To install services run:
$ source bin/install-services $ source bin/install-services
This will install the required node versions and run \`npm install\` for each service. This will install the required node versions and run \`npm install\` for each service.
See https://github.com/sharelatex/sharelatex/pull/549 for more info.\ See https://github.com/sharelatex/sharelatex/pull/549 for more info.\
` `)
); return callback()
return callback();
}, },
checkMake(callback) { checkMake(callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
grunt.log.write("Checking make is installed... "); callback = function (error) {}
return exec("make --version", function(error, stdout, stderr) { }
if ((error != null) && error.message.match("not found")) { grunt.log.write('Checking make is installed... ')
grunt.log.error("FAIL."); return exec('make --version', function (error, stdout, stderr) {
if (error != null && error.message.match('not found')) {
grunt.log.error('FAIL.')
grunt.log.errorlns(`\ grunt.log.errorlns(`\
Either make is not installed or is not in your path. Either make is not installed or is not in your path.
@ -238,31 +275,32 @@ On Ubuntu you can install make with:
sudo apt-get install build-essential sudo apt-get install build-essential
\ \
` `)
); return callback(error)
return callback(error);
} else if (error != null) { } else if (error != null) {
return callback(error); return callback(error)
} else { } else {
grunt.log.write("OK."); grunt.log.write('OK.')
return callback(); return callback()
} }
}); })
}, },
checkMongoConnect(callback) { checkMongoConnect(callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
grunt.log.write("Checking can connect to mongo"); callback = function (error) {}
const mongojs = require("mongojs");
const db = mongojs(settings.mongo.url, ["tags"]);
db.runCommand({ ping: 1 }, function(err, res) {
if (!err && res.ok) {
grunt.log.write("OK.");
} }
return callback(); grunt.log.write('Checking can connect to mongo')
}); const mongojs = require('mongojs')
return db.on('error', function(err){ const db = mongojs(settings.mongo.url, ['tags'])
err = "Can not connect to mongodb"; db.runCommand({ ping: 1 }, function (err, res) {
grunt.log.error("FAIL."); if (!err && res.ok) {
grunt.log.write('OK.')
}
return callback()
})
return db.on('error', function (err) {
err = 'Can not connect to mongodb'
grunt.log.error('FAIL.')
grunt.log.errorlns(`\ grunt.log.errorlns(`\
!!!!!!!!!!!!!! MONGO ERROR !!!!!!!!!!!!!! !!!!!!!!!!!!!! MONGO ERROR !!!!!!!!!!!!!!
@ -271,29 +309,30 @@ ShareLaTeX can not talk to the mongodb instance
Check the mongodb instance is running and accessible on env var SHARELATEX_MONGO_URL Check the mongodb instance is running and accessible on env var SHARELATEX_MONGO_URL
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\
` `)
); throw new Error('Can not connect to Mongodb')
throw new Error("Can not connect to Mongodb"); return callback(err)
return callback(err); })
});
}, },
checkRedisConnect(callback) { checkRedisConnect(callback) {
if (callback == null) { callback = function(error) {}; } if (callback == null) {
grunt.log.write("Checking can connect to redis\n"); callback = function (error) {}
const rclient = require("redis").createClient(settings.redis.web);
rclient.ping(function(err, res) {
if ((err == null)) {
grunt.log.write("OK.");
} else {
throw new Error("Can not connect to redis");
} }
return callback(); grunt.log.write('Checking can connect to redis\n')
}); const rclient = require('redis').createClient(settings.redis.web)
const errorHandler = _.once(function(err){
err = "Can not connect to redis"; rclient.ping(function (err, res) {
grunt.log.error("FAIL."); if (err == null) {
grunt.log.write('OK.')
} else {
throw new Error('Can not connect to redis')
}
return callback()
})
const errorHandler = _.once(function (err) {
err = 'Can not connect to redis'
grunt.log.error('FAIL.')
grunt.log.errorlns(`\ grunt.log.errorlns(`\
!!!!!!!!!!!!!! REDIS ERROR !!!!!!!!!!!!!! !!!!!!!!!!!!!! REDIS ERROR !!!!!!!!!!!!!!
@ -302,19 +341,17 @@ ShareLaTeX can not talk to the redis instance
Check the redis instance is running and accessible on env var SHARELATEX_REDIS_HOST Check the redis instance is running and accessible on env var SHARELATEX_REDIS_HOST
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\
` `)
); throw new Error('Can not connect to redis')
throw new Error("Can not connect to redis"); return callback(err)
return callback(err); })
}); return rclient.on('error', errorHandler)
return rclient.on('error', errorHandler); },
} })
}; }
};
function __guard__(value, transform) { function __guard__(value, transform) {
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; return typeof value !== 'undefined' && value !== null
? transform(value)
: undefined
} }

View file

@ -12,46 +12,45 @@
* DS207: Consider shorter variations of null checks * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
let allTexLiveDockerImageNames, allTexLiveDockerImages, redisConfig, siteUrl; let allTexLiveDockerImageNames, allTexLiveDockerImages, redisConfig, siteUrl
let e; let e
const Path = require('path'); const Path = require('path')
// These credentials are used for authenticating api requests // These credentials are used for authenticating api requests
// between services that may need to go over public channels // between services that may need to go over public channels
const httpAuthUser = "sharelatex"; const httpAuthUser = 'sharelatex'
const httpAuthPass = process.env.WEB_API_PASSWORD; const httpAuthPass = process.env.WEB_API_PASSWORD
const httpAuthUsers = {}; const httpAuthUsers = {}
httpAuthUsers[httpAuthUser] = httpAuthPass; httpAuthUsers[httpAuthUser] = httpAuthPass
const parse = function(option){ const parse = function (option) {
if (option != null) { if (option != null) {
try { try {
const opt = JSON.parse(option); const opt = JSON.parse(option)
return opt; return opt
} catch (err) { } catch (err) {
throw new Error(`problem parsing ${option}, invalid JSON`); throw new Error(`problem parsing ${option}, invalid JSON`)
} }
} }
}; }
const parseIntOrFail = function(value){ const parseIntOrFail = function (value) {
const parsedValue = parseInt(value, 10); const parsedValue = parseInt(value, 10)
if (isNaN(parsedValue)) { if (isNaN(parsedValue)) {
throw new Error(`'${value}' is an invalid integer`); throw new Error(`'${value}' is an invalid integer`)
} }
return parsedValue; return parsedValue
}; }
const DATA_DIR = '/var/lib/sharelatex/data'; const DATA_DIR = '/var/lib/sharelatex/data'
const TMP_DIR = '/var/lib/sharelatex/tmp'; const TMP_DIR = '/var/lib/sharelatex/tmp'
const settings = { const settings = {
clsi: { clsi: {
optimiseInDocker: process.env.OPTIMISE_PDF === 'true' optimiseInDocker: process.env.OPTIMISE_PDF === 'true',
}, },
brandPrefix: "", brandPrefix: '',
allowAnonymousReadAndWriteSharing: allowAnonymousReadAndWriteSharing:
process.env.SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING === 'true', process.env.SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING === 'true',
@ -66,7 +65,7 @@ const settings = {
// //
// The following works out of the box with Mongo's default settings: // The following works out of the box with Mongo's default settings:
mongo: { mongo: {
url : process.env.SHARELATEX_MONGO_URL || 'mongodb://dockerhost/sharelatex' url: process.env.SHARELATEX_MONGO_URL || 'mongodb://dockerhost/sharelatex',
}, },
// Redis is used in ShareLaTeX for high volume queries, like real-time // Redis is used in ShareLaTeX for high volume queries, like real-time
@ -75,36 +74,72 @@ const settings = {
// The following config will work with Redis's default settings: // The following config will work with Redis's default settings:
redis: { redis: {
web: (redisConfig = { web: (redisConfig = {
host: process.env.SHARELATEX_REDIS_HOST || "dockerhost", host: process.env.SHARELATEX_REDIS_HOST || 'dockerhost',
port: process.env.SHARELATEX_REDIS_PORT || "6379", port: process.env.SHARELATEX_REDIS_PORT || '6379',
password: process.env.SHARELATEX_REDIS_PASS || undefined, password: process.env.SHARELATEX_REDIS_PASS || undefined,
key_schema: { key_schema: {
// document-updater // document-updater
blockingKey({doc_id}) { return `Blocking:${doc_id}`; }, blockingKey({ doc_id }) {
docLines({doc_id}) { return `doclines:${doc_id}`; }, return `Blocking:${doc_id}`
docOps({doc_id}) { return `DocOps:${doc_id}`; }, },
docVersion({doc_id}) { return `DocVersion:${doc_id}`; }, docLines({ doc_id }) {
docHash({doc_id}) { return `DocHash:${doc_id}`; }, return `doclines:${doc_id}`
projectKey({doc_id}) { return `ProjectId:${doc_id}`; }, },
docsInProject({project_id}) { return `DocsIn:${project_id}`; }, docOps({ doc_id }) {
ranges({doc_id}) { return `Ranges:${doc_id}`; }, return `DocOps:${doc_id}`
},
docVersion({ doc_id }) {
return `DocVersion:${doc_id}`
},
docHash({ doc_id }) {
return `DocHash:${doc_id}`
},
projectKey({ doc_id }) {
return `ProjectId:${doc_id}`
},
docsInProject({ project_id }) {
return `DocsIn:${project_id}`
},
ranges({ doc_id }) {
return `Ranges:${doc_id}`
},
// document-updater:realtime // document-updater:realtime
pendingUpdates({doc_id}) { return `PendingUpdates:${doc_id}`; }, pendingUpdates({ doc_id }) {
return `PendingUpdates:${doc_id}`
},
// document-updater:history // document-updater:history
uncompressedHistoryOps({doc_id}) { return `UncompressedHistoryOps:${doc_id}`; }, uncompressedHistoryOps({ doc_id }) {
docsWithHistoryOps({project_id}) { return `DocsWithHistoryOps:${project_id}`; }, return `UncompressedHistoryOps:${doc_id}`
},
docsWithHistoryOps({ project_id }) {
return `DocsWithHistoryOps:${project_id}`
},
// document-updater:lock // document-updater:lock
blockingKey({doc_id}) { return `Blocking:${doc_id}`; }, blockingKey({ doc_id }) {
return `Blocking:${doc_id}`
},
// track-changes:lock // track-changes:lock
historyLock({doc_id}) { return `HistoryLock:${doc_id}`; }, historyLock({ doc_id }) {
historyIndexLock({project_id}) { return `HistoryIndexLock:${project_id}`; }, return `HistoryLock:${doc_id}`
},
historyIndexLock({ project_id }) {
return `HistoryIndexLock:${project_id}`
},
// track-changes:history // track-changes:history
uncompressedHistoryOps({doc_id}) { return `UncompressedHistoryOps:${doc_id}`; }, uncompressedHistoryOps({ doc_id }) {
docsWithHistoryOps({project_id}) { return `DocsWithHistoryOps:${project_id}`; }, return `UncompressedHistoryOps:${doc_id}`
},
docsWithHistoryOps({ project_id }) {
return `DocsWithHistoryOps:${project_id}`
},
// realtime // realtime
clientsInProject({project_id}) { return `clients_in_project:${project_id}`; }, clientsInProject({ project_id }) {
connectedUser({project_id, client_id}){ return `connected_user:${project_id}:${client_id}`; } return `clients_in_project:${project_id}`
} },
connectedUser({ project_id, client_id }) {
return `connected_user:${project_id}:${client_id}`
},
},
}), }),
fairy: redisConfig, fairy: redisConfig,
// track-changes and document-updater // track-changes and document-updater
@ -115,7 +150,7 @@ const settings = {
websessions: redisConfig, websessions: redisConfig,
api: redisConfig, api: redisConfig,
pubsub: redisConfig, pubsub: redisConfig,
project_history: redisConfig project_history: redisConfig,
}, },
// The compile server (the clsi) uses a SQL database to cache files and // The compile server (the clsi) uses a SQL database to cache files and
@ -129,12 +164,12 @@ const settings = {
// //
mysql: { mysql: {
clsi: { clsi: {
database: "clsi", database: 'clsi',
username: "clsi", username: 'clsi',
password: "", password: '',
dialect: "sqlite", dialect: 'sqlite',
storage: Path.join(DATA_DIR, "db.sqlite") storage: Path.join(DATA_DIR, 'db.sqlite'),
} },
}, },
// File storage // File storage
@ -143,11 +178,11 @@ const settings = {
// ShareLaTeX can store binary files like images either locally or in Amazon // ShareLaTeX can store binary files like images either locally or in Amazon
// S3. The default is locally: // S3. The default is locally:
filestore: { filestore: {
backend: "fs", backend: 'fs',
stores: { stores: {
user_files: Path.join(DATA_DIR, "user_files"), user_files: Path.join(DATA_DIR, 'user_files'),
template_files: Path.join(DATA_DIR, "template_files") template_files: Path.join(DATA_DIR, 'template_files'),
} },
}, },
// To use Amazon S3 as a storage backend, comment out the above config, and // To use Amazon S3 as a storage backend, comment out the above config, and
@ -163,7 +198,7 @@ const settings = {
// //
trackchanges: { trackchanges: {
continueOnError: true continueOnError: true,
}, },
// Local disk caching // Local disk caching
@ -172,15 +207,15 @@ const settings = {
// If we ever need to write something to disk (e.g. incoming requests // If we ever need to write something to disk (e.g. incoming requests
// that need processing but may be too big for memory), then write // that need processing but may be too big for memory), then write
// them to disk here: // them to disk here:
dumpFolder: Path.join(TMP_DIR, "dumpFolder"), dumpFolder: Path.join(TMP_DIR, 'dumpFolder'),
// Where to write uploads before they are processed // Where to write uploads before they are processed
uploadFolder: Path.join(TMP_DIR, "uploads"), uploadFolder: Path.join(TMP_DIR, 'uploads'),
// Where to write the project to disk before running LaTeX on it // Where to write the project to disk before running LaTeX on it
compilesDir: Path.join(DATA_DIR, "compiles"), compilesDir: Path.join(DATA_DIR, 'compiles'),
// Where to cache downloaded URLs for the CLSI // Where to cache downloaded URLs for the CLSI
clsiCacheDir: Path.join(DATA_DIR, "cache"), clsiCacheDir: Path.join(DATA_DIR, 'cache'),
// Where to write the output files to disk after running LaTeX // Where to write the output files to disk after running LaTeX
outputDir: Path.join(DATA_DIR, "output") outputDir: Path.join(DATA_DIR, 'output'),
}, },
// Server Config // Server Config
@ -191,23 +226,27 @@ const settings = {
siteUrl: (siteUrl = process.env.SHARELATEX_SITE_URL || 'http://localhost'), siteUrl: (siteUrl = process.env.SHARELATEX_SITE_URL || 'http://localhost'),
// The name this is used to describe your ShareLaTeX Installation // The name this is used to describe your ShareLaTeX Installation
appName: process.env.SHARELATEX_APP_NAME || "ShareLaTeX (Community Edition)", appName: process.env.SHARELATEX_APP_NAME || 'ShareLaTeX (Community Edition)',
restrictInvitesToExistingAccounts: process.env.SHARELATEX_RESTRICT_INVITES_TO_EXISTING_ACCOUNTS === 'true', restrictInvitesToExistingAccounts:
process.env.SHARELATEX_RESTRICT_INVITES_TO_EXISTING_ACCOUNTS === 'true',
nav: { nav: {
title: process.env.SHARELATEX_NAV_TITLE || process.env.SHARELATEX_APP_NAME || "ShareLaTeX Community Edition" title:
process.env.SHARELATEX_NAV_TITLE ||
process.env.SHARELATEX_APP_NAME ||
'ShareLaTeX Community Edition',
}, },
// The email address which users will be directed to as the main point of // The email address which users will be directed to as the main point of
// contact for this installation of ShareLaTeX. // contact for this installation of ShareLaTeX.
adminEmail: process.env.SHARELATEX_ADMIN_EMAIL || "placeholder@example.com", adminEmail: process.env.SHARELATEX_ADMIN_EMAIL || 'placeholder@example.com',
// If provided, a sessionSecret is used to sign cookies so that they cannot be // If provided, a sessionSecret is used to sign cookies so that they cannot be
// spoofed. This is recommended. // spoofed. This is recommended.
security: { security: {
sessionSecret: process.env.SHARELATEX_SESSION_SECRET || process.env.CRYPTO_RANDOM sessionSecret:
process.env.SHARELATEX_SESSION_SECRET || process.env.CRYPTO_RANDOM,
}, },
// These credentials are used for authenticating api requests // These credentials are used for authenticating api requests
@ -224,7 +263,7 @@ const settings = {
// If you are running ShareLaTeX over https, set this to true to send the // If you are running ShareLaTeX over https, set this to true to send the
// cookie with a secure flag (recommended). // cookie with a secure flag (recommended).
secureCookie: (process.env.SHARELATEX_SECURE_COOKIE != null), secureCookie: process.env.SHARELATEX_SECURE_COOKIE != null,
// If you are running ShareLaTeX behind a proxy (like Apache, Nginx, etc) // If you are running ShareLaTeX behind a proxy (like Apache, Nginx, etc)
// then set this to true to allow it to correctly detect the forwarded IP // then set this to true to allow it to correctly detect the forwarded IP
@ -234,61 +273,64 @@ const settings = {
i18n: { i18n: {
subdomainLang: { subdomainLang: {
www: {lngCode:process.env.SHARELATEX_SITE_LANGUAGE || "en", url: siteUrl} www: {
lngCode: process.env.SHARELATEX_SITE_LANGUAGE || 'en',
url: siteUrl,
}, },
defaultLng: process.env.SHARELATEX_SITE_LANGUAGE || "en" },
defaultLng: process.env.SHARELATEX_SITE_LANGUAGE || 'en',
}, },
currentImageName: process.env.TEX_LIVE_DOCKER_IMAGE, currentImageName: process.env.TEX_LIVE_DOCKER_IMAGE,
apis: { apis: {
web: { web: {
url: "http://localhost:3000", url: 'http://localhost:3000',
user: httpAuthUser, user: httpAuthUser,
pass: httpAuthPass pass: httpAuthPass,
}, },
project_history: { project_history: {
enabled: false enabled: false,
}
}, },
references:{}, },
notifications:undefined, references: {},
notifications: undefined,
defaultFeatures: { defaultFeatures: {
collaborators: -1, collaborators: -1,
dropbox: true, dropbox: true,
versioning: true, versioning: true,
compileTimeout: parseIntOrFail(process.env.COMPILE_TIMEOUT || 180), compileTimeout: parseIntOrFail(process.env.COMPILE_TIMEOUT || 180),
compileGroup: "standard", compileGroup: 'standard',
trackChanges: true, trackChanges: true,
templates: true, templates: true,
references: true references: true,
} },
}; }
// # OPTIONAL CONFIGURABLE SETTINGS // # OPTIONAL CONFIGURABLE SETTINGS
if (process.env.SHARELATEX_LEFT_FOOTER != null) { if (process.env.SHARELATEX_LEFT_FOOTER != null) {
try { try {
settings.nav.left_footer = JSON.parse(process.env.SHARELATEX_LEFT_FOOTER); settings.nav.left_footer = JSON.parse(process.env.SHARELATEX_LEFT_FOOTER)
} catch (error) { } catch (error) {
e = error; e = error
console.error("could not parse SHARELATEX_LEFT_FOOTER, not valid JSON"); console.error('could not parse SHARELATEX_LEFT_FOOTER, not valid JSON')
} }
} }
if (process.env.SHARELATEX_RIGHT_FOOTER != null) { if (process.env.SHARELATEX_RIGHT_FOOTER != null) {
settings.nav.right_footer = process.env.SHARELATEX_RIGHT_FOOTER; settings.nav.right_footer = process.env.SHARELATEX_RIGHT_FOOTER
try { try {
settings.nav.right_footer = JSON.parse(process.env.SHARELATEX_RIGHT_FOOTER); settings.nav.right_footer = JSON.parse(process.env.SHARELATEX_RIGHT_FOOTER)
} catch (error1) { } catch (error1) {
e = error1; e = error1
console.error("could not parse SHARELATEX_RIGHT_FOOTER, not valid JSON"); console.error('could not parse SHARELATEX_RIGHT_FOOTER, not valid JSON')
} }
} }
if (process.env.SHARELATEX_HEADER_IMAGE_URL != null) { if (process.env.SHARELATEX_HEADER_IMAGE_URL != null) {
settings.nav.custom_logo = process.env.SHARELATEX_HEADER_IMAGE_URL; settings.nav.custom_logo = process.env.SHARELATEX_HEADER_IMAGE_URL
} }
if (process.env.SHARELATEX_HEADER_NAV_LINKS != null) { if (process.env.SHARELATEX_HEADER_NAV_LINKS != null) {
@ -299,21 +341,20 @@ if (process.env.SHARELATEX_HEADER_NAV_LINKS != null) {
# See https://github.com/sharelatex/sharelatex/wiki/Configuring-Headers,-Footers-&-Logo # See https://github.com/sharelatex/sharelatex/wiki/Configuring-Headers,-Footers-&-Logo
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\
` `)
);
} }
if (process.env.SHARELATEX_HEADER_EXTRAS != null) { if (process.env.SHARELATEX_HEADER_EXTRAS != null) {
try { try {
settings.nav.header_extras = JSON.parse(process.env.SHARELATEX_HEADER_EXTRAS); settings.nav.header_extras = JSON.parse(
process.env.SHARELATEX_HEADER_EXTRAS
)
} catch (error2) { } catch (error2) {
e = error2; e = error2
console.error("could not parse SHARELATEX_HEADER_EXTRAS, not valid JSON"); console.error('could not parse SHARELATEX_HEADER_EXTRAS, not valid JSON')
} }
} }
// Sending Email // Sending Email
// ------------- // -------------
// //
@ -323,12 +364,10 @@ if (process.env.SHARELATEX_HEADER_EXTRAS != null) {
// //
// http://www.nodemailer.com/docs/transports // http://www.nodemailer.com/docs/transports
if (process.env.SHARELATEX_EMAIL_FROM_ADDRESS != null) { if (process.env.SHARELATEX_EMAIL_FROM_ADDRESS != null) {
settings.email = { settings.email = {
fromAddress: process.env.SHARELATEX_EMAIL_FROM_ADDRESS, fromAddress: process.env.SHARELATEX_EMAIL_FROM_ADDRESS,
replyTo: process.env.SHARELATEX_EMAIL_REPLY_TO || "", replyTo: process.env.SHARELATEX_EMAIL_REPLY_TO || '',
driver: process.env.SHARELATEX_EMAIL_DRIVER, driver: process.env.SHARELATEX_EMAIL_DRIVER,
parameters: { parameters: {
// AWS Creds // AWS Creds
@ -341,65 +380,73 @@ if (process.env.SHARELATEX_EMAIL_FROM_ADDRESS != null) {
secure: parse(process.env.SHARELATEX_EMAIL_SMTP_SECURE), secure: parse(process.env.SHARELATEX_EMAIL_SMTP_SECURE),
ignoreTLS: parse(process.env.SHARELATEX_EMAIL_SMTP_IGNORE_TLS), ignoreTLS: parse(process.env.SHARELATEX_EMAIL_SMTP_IGNORE_TLS),
name: process.env.SHARELATEX_EMAIL_SMTP_NAME, name: process.env.SHARELATEX_EMAIL_SMTP_NAME,
logger: process.env.SHARELATEX_EMAIL_SMTP_LOGGER === 'true' logger: process.env.SHARELATEX_EMAIL_SMTP_LOGGER === 'true',
}, },
textEncoding: process.env.SHARELATEX_EMAIL_TEXT_ENCODING, textEncoding: process.env.SHARELATEX_EMAIL_TEXT_ENCODING,
template: { template: {
customFooter: process.env.SHARELATEX_CUSTOM_EMAIL_FOOTER customFooter: process.env.SHARELATEX_CUSTOM_EMAIL_FOOTER,
},
} }
};
if (process.env.SHARELATEX_EMAIL_AWS_SES_REGION != null) { if (process.env.SHARELATEX_EMAIL_AWS_SES_REGION != null) {
settings.email.parameters.region = process.env.SHARELATEX_EMAIL_AWS_SES_REGION; settings.email.parameters.region =
process.env.SHARELATEX_EMAIL_AWS_SES_REGION
} }
if ((process.env.SHARELATEX_EMAIL_SMTP_USER != null) || (process.env.SHARELATEX_EMAIL_SMTP_PASS != null)) { if (
process.env.SHARELATEX_EMAIL_SMTP_USER != null ||
process.env.SHARELATEX_EMAIL_SMTP_PASS != null
) {
settings.email.parameters.auth = { settings.email.parameters.auth = {
user: process.env.SHARELATEX_EMAIL_SMTP_USER, user: process.env.SHARELATEX_EMAIL_SMTP_USER,
pass: process.env.SHARELATEX_EMAIL_SMTP_PASS pass: process.env.SHARELATEX_EMAIL_SMTP_PASS,
}; }
} }
if (process.env.SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH != null) { if (process.env.SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH != null) {
settings.email.parameters.tls = settings.email.parameters.tls = {
{rejectUnauthorized: parse(process.env.SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH)}; rejectUnauthorized: parse(
process.env.SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH
),
}
} }
} }
// i18n // i18n
if (process.env.SHARELATEX_LANG_DOMAIN_MAPPING != null) { if (process.env.SHARELATEX_LANG_DOMAIN_MAPPING != null) {
settings.i18n.subdomainLang = parse(
settings.i18n.subdomainLang = parse(process.env.SHARELATEX_LANG_DOMAIN_MAPPING); process.env.SHARELATEX_LANG_DOMAIN_MAPPING
)
} }
// Password Settings // Password Settings
// ----------- // -----------
// These restrict the passwords users can use when registering // These restrict the passwords users can use when registering
// opts are from http://antelle.github.io/passfield // opts are from http://antelle.github.io/passfield
if (process.env.SHARELATEX_PASSWORD_VALIDATION_PATTERN || process.env.SHARELATEX_PASSWORD_VALIDATION_MIN_LENGTH || process.env.SHARELATEX_PASSWORD_VALIDATION_MAX_LENGTH) { if (
process.env.SHARELATEX_PASSWORD_VALIDATION_PATTERN ||
process.env.SHARELATEX_PASSWORD_VALIDATION_MIN_LENGTH ||
process.env.SHARELATEX_PASSWORD_VALIDATION_MAX_LENGTH
) {
settings.passwordStrengthOptions = { settings.passwordStrengthOptions = {
pattern: process.env.SHARELATEX_PASSWORD_VALIDATION_PATTERN || "aA$3", pattern: process.env.SHARELATEX_PASSWORD_VALIDATION_PATTERN || 'aA$3',
length: {min:process.env.SHARELATEX_PASSWORD_VALIDATION_MIN_LENGTH || 8, max: process.env.SHARELATEX_PASSWORD_VALIDATION_MAX_LENGTH || 150} length: {
}; min: process.env.SHARELATEX_PASSWORD_VALIDATION_MIN_LENGTH || 8,
max: process.env.SHARELATEX_PASSWORD_VALIDATION_MAX_LENGTH || 150,
},
}
} }
// ###################### // ######################
// ShareLaTeX Server Pro // ShareLaTeX Server Pro
// ###################### // ######################
if (parse(process.env.SHARELATEX_IS_SERVER_PRO) === true) { if (parse(process.env.SHARELATEX_IS_SERVER_PRO) === true) {
settings.bypassPercentageRollouts = true; settings.bypassPercentageRollouts = true
settings.apis.references = settings.apis.references = { url: 'http://localhost:3040' }
{url: "http://localhost:3040"};
} }
// LDAP - SERVER PRO ONLY // LDAP - SERVER PRO ONLY
// ---------- // ----------
@ -411,18 +458,21 @@ if (process.env.SHARELATEX_LDAP_HOST) {
# See https://github.com/sharelatex/sharelatex/wiki/Server-Pro:-LDAP-Config # See https://github.com/sharelatex/sharelatex/wiki/Server-Pro:-LDAP-Config
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\
` `)
);
} }
if (process.env.SHARELATEX_LDAP_URL) { if (process.env.SHARELATEX_LDAP_URL) {
let _ldap_connect_timeout, _ldap_group_search_attribs, _ldap_search_attribs, _ldap_timeout; let _ldap_connect_timeout,
settings.externalAuth = true; _ldap_group_search_attribs,
_ldap_search_attribs,
_ldap_timeout
settings.externalAuth = true
settings.ldap = { settings.ldap = {
emailAtt: process.env.SHARELATEX_LDAP_EMAIL_ATT, emailAtt: process.env.SHARELATEX_LDAP_EMAIL_ATT,
nameAtt: process.env.SHARELATEX_LDAP_NAME_ATT, nameAtt: process.env.SHARELATEX_LDAP_NAME_ATT,
lastNameAtt: process.env.SHARELATEX_LDAP_LAST_NAME_ATT, lastNameAtt: process.env.SHARELATEX_LDAP_LAST_NAME_ATT,
updateUserDetailsOnLogin: process.env.SHARELATEX_LDAP_UPDATE_USER_DETAILS_ON_LOGIN === 'true', updateUserDetailsOnLogin:
process.env.SHARELATEX_LDAP_UPDATE_USER_DETAILS_ON_LOGIN === 'true',
placeholder: process.env.SHARELATEX_LDAP_PLACEHOLDER, placeholder: process.env.SHARELATEX_LDAP_PLACEHOLDER,
server: { server: {
url: process.env.SHARELATEX_LDAP_URL, url: process.env.SHARELATEX_LDAP_URL,
@ -432,94 +482,108 @@ if (process.env.SHARELATEX_LDAP_URL) {
searchBase: process.env.SHARELATEX_LDAP_SEARCH_BASE, searchBase: process.env.SHARELATEX_LDAP_SEARCH_BASE,
searchScope: process.env.SHARELATEX_LDAP_SEARCH_SCOPE, searchScope: process.env.SHARELATEX_LDAP_SEARCH_SCOPE,
searchFilter: process.env.SHARELATEX_LDAP_SEARCH_FILTER, searchFilter: process.env.SHARELATEX_LDAP_SEARCH_FILTER,
searchAttributes: ( searchAttributes: (_ldap_search_attribs =
(_ldap_search_attribs = process.env.SHARELATEX_LDAP_SEARCH_ATTRIBUTES) ? process.env.SHARELATEX_LDAP_SEARCH_ATTRIBUTES)
(() => { try { ? (() => {
return JSON.parse(_ldap_search_attribs); try {
return JSON.parse(_ldap_search_attribs)
} catch (error3) { } catch (error3) {
e = error3; e = error3
return console.error("could not parse SHARELATEX_LDAP_SEARCH_ATTRIBUTES"); return console.error(
} })() 'could not parse SHARELATEX_LDAP_SEARCH_ATTRIBUTES'
: )
undefined }
), })()
: undefined,
groupDnProperty: process.env.SHARELATEX_LDAP_GROUP_DN_PROPERTY, groupDnProperty: process.env.SHARELATEX_LDAP_GROUP_DN_PROPERTY,
groupSearchBase: process.env.SHARELATEX_LDAP_GROUP_SEARCH_BASE, groupSearchBase: process.env.SHARELATEX_LDAP_GROUP_SEARCH_BASE,
groupSearchScope: process.env.SHARELATEX_LDAP_GROUP_SEARCH_SCOPE, groupSearchScope: process.env.SHARELATEX_LDAP_GROUP_SEARCH_SCOPE,
groupSearchFilter: process.env.SHARELATEX_LDAP_GROUP_SEARCH_FILTER, groupSearchFilter: process.env.SHARELATEX_LDAP_GROUP_SEARCH_FILTER,
groupSearchAttributes: ( groupSearchAttributes: (_ldap_group_search_attribs =
(_ldap_group_search_attribs = process.env.SHARELATEX_LDAP_GROUP_SEARCH_ATTRIBUTES) ? process.env.SHARELATEX_LDAP_GROUP_SEARCH_ATTRIBUTES)
(() => { try { ? (() => {
return JSON.parse(_ldap_group_search_attribs); try {
return JSON.parse(_ldap_group_search_attribs)
} catch (error4) { } catch (error4) {
e = error4; e = error4
return console.error("could not parse SHARELATEX_LDAP_GROUP_SEARCH_ATTRIBUTES"); return console.error(
} })() 'could not parse SHARELATEX_LDAP_GROUP_SEARCH_ATTRIBUTES'
:
undefined
),
cache: process.env.SHARELATEX_LDAP_CACHE === 'true',
timeout: (
(_ldap_timeout = process.env.SHARELATEX_LDAP_TIMEOUT) ?
(() => { try {
return parseIntOrFail(_ldap_timeout);
} catch (error5) {
e = error5;
return console.error("Cannot parse SHARELATEX_LDAP_TIMEOUT");
} })()
:
undefined
),
connectTimeout: (
(_ldap_connect_timeout = process.env.SHARELATEX_LDAP_CONNECT_TIMEOUT) ?
(() => { try {
return parseIntOrFail(_ldap_connect_timeout);
} catch (error6) {
e = error6;
return console.error("Cannot parse SHARELATEX_LDAP_CONNECT_TIMEOUT");
} })()
:
undefined
) )
} }
}; })()
: undefined,
if (process.env.SHARELATEX_LDAP_TLS_OPTS_CA_PATH) { cache: process.env.SHARELATEX_LDAP_CACHE === 'true',
let ca, ca_paths; timeout: (_ldap_timeout = process.env.SHARELATEX_LDAP_TIMEOUT)
? (() => {
try { try {
ca = JSON.parse(process.env.SHARELATEX_LDAP_TLS_OPTS_CA_PATH); return parseIntOrFail(_ldap_timeout)
} catch (error7) { } catch (error5) {
e = error7; e = error5
console.error("could not parse SHARELATEX_LDAP_TLS_OPTS_CA_PATH, invalid JSON"); return console.error('Cannot parse SHARELATEX_LDAP_TIMEOUT')
}
})()
: undefined,
connectTimeout: (_ldap_connect_timeout =
process.env.SHARELATEX_LDAP_CONNECT_TIMEOUT)
? (() => {
try {
return parseIntOrFail(_ldap_connect_timeout)
} catch (error6) {
e = error6
return console.error(
'Cannot parse SHARELATEX_LDAP_CONNECT_TIMEOUT'
)
}
})()
: undefined,
},
} }
if (typeof(ca) === 'string') { if (process.env.SHARELATEX_LDAP_TLS_OPTS_CA_PATH) {
ca_paths = [ca]; let ca, ca_paths
} else if ((typeof(ca) === 'object') && ((ca != null ? ca.length : undefined) != null)) { try {
ca_paths = ca; ca = JSON.parse(process.env.SHARELATEX_LDAP_TLS_OPTS_CA_PATH)
} catch (error7) {
e = error7
console.error(
'could not parse SHARELATEX_LDAP_TLS_OPTS_CA_PATH, invalid JSON'
)
}
if (typeof ca === 'string') {
ca_paths = [ca]
} else if (
typeof ca === 'object' &&
(ca != null ? ca.length : undefined) != null
) {
ca_paths = ca
} else { } else {
console.error("problem parsing SHARELATEX_LDAP_TLS_OPTS_CA_PATH"); console.error('problem parsing SHARELATEX_LDAP_TLS_OPTS_CA_PATH')
} }
settings.ldap.server.tlsOptions = { settings.ldap.server.tlsOptions = {
rejectUnauthorized: process.env.SHARELATEX_LDAP_TLS_OPTS_REJECT_UNAUTH === "true", rejectUnauthorized:
ca:ca_paths // e.g.'/etc/ldap/ca_certs.pem' process.env.SHARELATEX_LDAP_TLS_OPTS_REJECT_UNAUTH === 'true',
}; ca: ca_paths, // e.g.'/etc/ldap/ca_certs.pem'
}
} }
} }
if (process.env.SHARELATEX_SAML_ENTRYPOINT) { if (process.env.SHARELATEX_SAML_ENTRYPOINT) {
// NOTE: see https://github.com/node-saml/passport-saml/blob/master/README.md for docs of `server` options // NOTE: see https://github.com/node-saml/passport-saml/blob/master/README.md for docs of `server` options
let _saml_additionalAuthorizeParams, _saml_additionalLogoutParams, _saml_additionalParams, _saml_expiration, _saml_skew; let _saml_additionalAuthorizeParams,
settings.externalAuth = true; _saml_additionalLogoutParams,
_saml_additionalParams,
_saml_expiration,
_saml_skew
settings.externalAuth = true
settings.saml = { settings.saml = {
updateUserDetailsOnLogin: process.env.SHARELATEX_SAML_UPDATE_USER_DETAILS_ON_LOGIN === 'true', updateUserDetailsOnLogin:
process.env.SHARELATEX_SAML_UPDATE_USER_DETAILS_ON_LOGIN === 'true',
identityServiceName: process.env.SHARELATEX_SAML_IDENTITY_SERVICE_NAME, identityServiceName: process.env.SHARELATEX_SAML_IDENTITY_SERVICE_NAME,
emailField: process.env.SHARELATEX_SAML_EMAIL_FIELD || process.env.SHARELATEX_SAML_EMAIL_FIELD_NAME, emailField:
process.env.SHARELATEX_SAML_EMAIL_FIELD ||
process.env.SHARELATEX_SAML_EMAIL_FIELD_NAME,
firstNameField: process.env.SHARELATEX_SAML_FIRST_NAME_FIELD, firstNameField: process.env.SHARELATEX_SAML_FIRST_NAME_FIELD,
lastNameField: process.env.SHARELATEX_SAML_LAST_NAME_FIELD, lastNameField: process.env.SHARELATEX_SAML_LAST_NAME_FIELD,
server: { server: {
@ -531,162 +595,184 @@ if (process.env.SHARELATEX_SAML_ENTRYPOINT) {
decryptionCert: process.env.SHARELATEX_SAML_DECRYPTION_CERT, decryptionCert: process.env.SHARELATEX_SAML_DECRYPTION_CERT,
signatureAlgorithm: process.env.SHARELATEX_SAML_SIGNATURE_ALGORITHM, signatureAlgorithm: process.env.SHARELATEX_SAML_SIGNATURE_ALGORITHM,
identifierFormat: process.env.SHARELATEX_SAML_IDENTIFIER_FORMAT, identifierFormat: process.env.SHARELATEX_SAML_IDENTIFIER_FORMAT,
attributeConsumingServiceIndex: process.env.SHARELATEX_SAML_ATTRIBUTE_CONSUMING_SERVICE_INDEX, attributeConsumingServiceIndex:
process.env.SHARELATEX_SAML_ATTRIBUTE_CONSUMING_SERVICE_INDEX,
authnContext: process.env.SHARELATEX_SAML_AUTHN_CONTEXT, authnContext: process.env.SHARELATEX_SAML_AUTHN_CONTEXT,
authnRequestBinding: process.env.SHARELATEX_SAML_AUTHN_REQUEST_BINDING, authnRequestBinding: process.env.SHARELATEX_SAML_AUTHN_REQUEST_BINDING,
validateInResponseTo: process.env.SHARELATEX_SAML_VALIDATE_IN_RESPONSE_TO, validateInResponseTo: process.env.SHARELATEX_SAML_VALIDATE_IN_RESPONSE_TO,
cacheProvider: process.env.SHARELATEX_SAML_CACHE_PROVIDER, cacheProvider: process.env.SHARELATEX_SAML_CACHE_PROVIDER,
logoutUrl: process.env.SHARELATEX_SAML_LOGOUT_URL, logoutUrl: process.env.SHARELATEX_SAML_LOGOUT_URL,
logoutCallbackUrl: process.env.SHARELATEX_SAML_LOGOUT_CALLBACK_URL, logoutCallbackUrl: process.env.SHARELATEX_SAML_LOGOUT_CALLBACK_URL,
disableRequestedAuthnContext: process.env.SHARELATEX_SAML_DISABLE_REQUESTED_AUTHN_CONTEXT === 'true', disableRequestedAuthnContext:
process.env.SHARELATEX_SAML_DISABLE_REQUESTED_AUTHN_CONTEXT === 'true',
forceAuthn: process.env.SHARELATEX_SAML_FORCE_AUTHN === 'true', forceAuthn: process.env.SHARELATEX_SAML_FORCE_AUTHN === 'true',
skipRequestCompression: process.env.SHARELATEX_SAML_SKIP_REQUEST_COMPRESSION === 'true', skipRequestCompression:
acceptedClockSkewMs: ( process.env.SHARELATEX_SAML_SKIP_REQUEST_COMPRESSION === 'true',
(_saml_skew = process.env.SHARELATEX_SAML_ACCEPTED_CLOCK_SKEW_MS) ? acceptedClockSkewMs: (_saml_skew =
(() => { try { process.env.SHARELATEX_SAML_ACCEPTED_CLOCK_SKEW_MS)
return parseIntOrFail(_saml_skew); ? (() => {
try {
return parseIntOrFail(_saml_skew)
} catch (error8) { } catch (error8) {
e = error8; e = error8
return console.error("Cannot parse SHARELATEX_SAML_ACCEPTED_CLOCK_SKEW_MS"); return console.error(
} })() 'Cannot parse SHARELATEX_SAML_ACCEPTED_CLOCK_SKEW_MS'
:
undefined
),
requestIdExpirationPeriodMs: (
(_saml_expiration = process.env.SHARELATEX_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS) ?
(() => { try {
return parseIntOrFail(_saml_expiration);
} catch (error9) {
e = error9;
return console.error("Cannot parse SHARELATEX_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS");
} })()
:
undefined
),
additionalParams: (
(_saml_additionalParams = process.env.SHARELATEX_SAML_ADDITIONAL_PARAMS) ?
(() => { try {
return JSON.parse(_saml_additionalParams);
} catch (error10) {
e = error10;
return console.error("Cannot parse SHARELATEX_SAML_ADDITIONAL_PARAMS");
} })()
:
undefined
),
additionalAuthorizeParams: (
(_saml_additionalAuthorizeParams = process.env.SHARELATEX_SAML_ADDITIONAL_AUTHORIZE_PARAMS) ?
(() => { try {
return JSON.parse(_saml_additionalAuthorizeParams );
} catch (error11) {
e = error11;
return console.error("Cannot parse SHARELATEX_SAML_ADDITIONAL_AUTHORIZE_PARAMS");
} })()
:
undefined
),
additionalLogoutParams: (
(_saml_additionalLogoutParams = process.env.SHARELATEX_SAML_ADDITIONAL_LOGOUT_PARAMS) ?
(() => { try {
return JSON.parse(_saml_additionalLogoutParams );
} catch (error12) {
e = error12;
return console.error("Cannot parse SHARELATEX_SAML_ADDITIONAL_LOGOUT_PARAMS");
} })()
:
undefined
) )
} }
}; })()
: undefined,
requestIdExpirationPeriodMs: (_saml_expiration =
process.env.SHARELATEX_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS)
? (() => {
try {
return parseIntOrFail(_saml_expiration)
} catch (error9) {
e = error9
return console.error(
'Cannot parse SHARELATEX_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS'
)
}
})()
: undefined,
additionalParams: (_saml_additionalParams =
process.env.SHARELATEX_SAML_ADDITIONAL_PARAMS)
? (() => {
try {
return JSON.parse(_saml_additionalParams)
} catch (error10) {
e = error10
return console.error(
'Cannot parse SHARELATEX_SAML_ADDITIONAL_PARAMS'
)
}
})()
: undefined,
additionalAuthorizeParams: (_saml_additionalAuthorizeParams =
process.env.SHARELATEX_SAML_ADDITIONAL_AUTHORIZE_PARAMS)
? (() => {
try {
return JSON.parse(_saml_additionalAuthorizeParams)
} catch (error11) {
e = error11
return console.error(
'Cannot parse SHARELATEX_SAML_ADDITIONAL_AUTHORIZE_PARAMS'
)
}
})()
: undefined,
additionalLogoutParams: (_saml_additionalLogoutParams =
process.env.SHARELATEX_SAML_ADDITIONAL_LOGOUT_PARAMS)
? (() => {
try {
return JSON.parse(_saml_additionalLogoutParams)
} catch (error12) {
e = error12
return console.error(
'Cannot parse SHARELATEX_SAML_ADDITIONAL_LOGOUT_PARAMS'
)
}
})()
: undefined,
},
}
// SHARELATEX_SAML_CERT cannot be empty // SHARELATEX_SAML_CERT cannot be empty
// https://github.com/node-saml/passport-saml/commit/f6b1c885c0717f1083c664345556b535f217c102 // https://github.com/node-saml/passport-saml/commit/f6b1c885c0717f1083c664345556b535f217c102
if (process.env.SHARELATEX_SAML_CERT) { if (process.env.SHARELATEX_SAML_CERT) {
settings.saml.server.cert = process.env.SHARELATEX_SAML_CERT; settings.saml.server.cert = process.env.SHARELATEX_SAML_CERT
settings.saml.server.privateCert = process.env.SHARELATEX_SAML_PRIVATE_CERT; settings.saml.server.privateCert = process.env.SHARELATEX_SAML_PRIVATE_CERT
} }
} }
// Compiler // Compiler
// -------- // --------
if (process.env.SANDBOXED_COMPILES === "true") { if (process.env.SANDBOXED_COMPILES === 'true') {
settings.clsi = { settings.clsi = {
dockerRunner: true, dockerRunner: true,
docker: { docker: {
image: process.env.TEX_LIVE_DOCKER_IMAGE, image: process.env.TEX_LIVE_DOCKER_IMAGE,
env: { env: {
HOME: "/tmp", HOME: '/tmp',
PATH: process.env.COMPILER_PATH || "/usr/local/texlive/2015/bin/x86_64-linux:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" PATH:
process.env.COMPILER_PATH ||
'/usr/local/texlive/2015/bin/x86_64-linux:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
},
user: 'www-data',
}, },
user: "www-data"
} }
};
if ((settings.path == null)) { if (settings.path == null) {
settings.path = {}; settings.path = {}
} }
settings.path.synctexBaseDir = () => "/compile"; settings.path.synctexBaseDir = () => '/compile'
if (process.env.SANDBOXED_COMPILES_SIBLING_CONTAINERS === 'true') { if (process.env.SANDBOXED_COMPILES_SIBLING_CONTAINERS === 'true') {
console.log("Using sibling containers for sandboxed compiles"); console.log('Using sibling containers for sandboxed compiles')
if (process.env.SANDBOXED_COMPILES_HOST_DIR) { if (process.env.SANDBOXED_COMPILES_HOST_DIR) {
settings.path.sandboxedCompilesHostDir = process.env.SANDBOXED_COMPILES_HOST_DIR; settings.path.sandboxedCompilesHostDir =
process.env.SANDBOXED_COMPILES_HOST_DIR
} else { } else {
console.error('Sibling containers, but SANDBOXED_COMPILES_HOST_DIR not set'); console.error(
'Sibling containers, but SANDBOXED_COMPILES_HOST_DIR not set'
)
} }
} }
} }
// Templates // Templates
// --------- // ---------
if (process.env.SHARELATEX_TEMPLATES_USER_ID) { if (process.env.SHARELATEX_TEMPLATES_USER_ID) {
settings.templates = { settings.templates = {
mountPointUrl: "/templates", mountPointUrl: '/templates',
user_id: process.env.SHARELATEX_TEMPLATES_USER_ID user_id: process.env.SHARELATEX_TEMPLATES_USER_ID,
}; }
settings.templateLinks = parse(process.env.SHARELATEX_NEW_PROJECT_TEMPLATE_LINKS); settings.templateLinks = parse(
process.env.SHARELATEX_NEW_PROJECT_TEMPLATE_LINKS
)
} }
// /Learn // /Learn
// ------- // -------
if (process.env.SHARELATEX_PROXY_LEARN != null) { if (process.env.SHARELATEX_PROXY_LEARN != null) {
settings.proxyLearn = parse(process.env.SHARELATEX_PROXY_LEARN); settings.proxyLearn = parse(process.env.SHARELATEX_PROXY_LEARN)
} }
// /References // /References
// ----------- // -----------
if (process.env.SHARELATEX_ELASTICSEARCH_URL != null) { if (process.env.SHARELATEX_ELASTICSEARCH_URL != null) {
settings.references.elasticsearch = settings.references.elasticsearch = {
{host: process.env.SHARELATEX_ELASTICSEARCH_URL}; host: process.env.SHARELATEX_ELASTICSEARCH_URL,
}
} }
// TeX Live Images // TeX Live Images
// ----------- // -----------
if (process.env.ALL_TEX_LIVE_DOCKER_IMAGES != null) { if (process.env.ALL_TEX_LIVE_DOCKER_IMAGES != null) {
allTexLiveDockerImages = process.env.ALL_TEX_LIVE_DOCKER_IMAGES.split(','); allTexLiveDockerImages = process.env.ALL_TEX_LIVE_DOCKER_IMAGES.split(',')
} }
if (process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES != null) { if (process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES != null) {
allTexLiveDockerImageNames = process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES.split(','); allTexLiveDockerImageNames =
process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES.split(',')
} }
if (allTexLiveDockerImages != null) { if (allTexLiveDockerImages != null) {
settings.allowedImageNames = []; settings.allowedImageNames = []
for (let index = 0; index < allTexLiveDockerImages.length; index++) { for (let index = 0; index < allTexLiveDockerImages.length; index++) {
const fullImageName = allTexLiveDockerImages[index]; const fullImageName = allTexLiveDockerImages[index]
const imageName = Path.basename(fullImageName); const imageName = Path.basename(fullImageName)
const imageDesc = (allTexLiveDockerImageNames != null) ? allTexLiveDockerImageNames[index] : imageName; const imageDesc =
settings.allowedImageNames.push({ imageName, imageDesc }); allTexLiveDockerImageNames != null
? allTexLiveDockerImageNames[index]
: imageName
settings.allowedImageNames.push({ imageName, imageDesc })
} }
} }
// With lots of incoming and outgoing HTTP connections to different services, // With lots of incoming and outgoing HTTP connections to different services,
// sometimes long running, it is a good idea to increase the default number // sometimes long running, it is a good idea to increase the default number
// of sockets that Node will hold open. // of sockets that Node will hold open.
const http = require('http'); const http = require('http')
http.globalAgent.maxSockets = 300; http.globalAgent.maxSockets = 300
const https = require('https'); const https = require('https')
https.globalAgent.maxSockets = 300; https.globalAgent.maxSockets = 300
module.exports = settings; module.exports = settings

View file

@ -11,35 +11,55 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/ */
module.exports = function(grunt) { module.exports = function (grunt) {
grunt.registerTask(
grunt.registerTask('user:create-admin', "Create a user with the given email address and make them an admin. Update in place if the user already exists. Usage: grunt user:create-admin --email joe@example.com", function() { 'user:create-admin',
const done = this.async(); 'Create a user with the given email address and make them an admin. Update in place if the user already exists. Usage: grunt user:create-admin --email joe@example.com',
const email = grunt.option("email"); function () {
if ((email == null)) { const done = this.async()
console.error("Usage: grunt user:create-admin --email=joe@example.com"); const email = grunt.option('email')
process.exit(1); if (email == null) {
console.error('Usage: grunt user:create-admin --email=joe@example.com')
process.exit(1)
} }
const settings = require("settings-sharelatex"); const settings = require('settings-sharelatex')
const mongodb = require("../web/app/src/infrastructure/mongodb"); const mongodb = require('../web/app/src/infrastructure/mongodb')
const UserRegistrationHandler = require("../web/app/src/Features/User/UserRegistrationHandler"); const UserRegistrationHandler = require('../web/app/src/Features/User/UserRegistrationHandler')
const OneTimeTokenHandler = require("../web/app/src/Features/Security/OneTimeTokenHandler"); const OneTimeTokenHandler = require('../web/app/src/Features/Security/OneTimeTokenHandler')
return mongodb.waitForDb().then(() => UserRegistrationHandler.registerNewUser({ return mongodb.waitForDb().then(() =>
UserRegistrationHandler.registerNewUser(
{
email, email,
password: require("crypto").randomBytes(32).toString("hex") password: require('crypto').randomBytes(32).toString('hex'),
}, function(error, user) { },
if ((error != null) && ((error != null ? error.message : undefined) !== "EmailAlreadyRegistered")) { function (error, user) {
throw error; if (
error != null &&
(error != null ? error.message : undefined) !==
'EmailAlreadyRegistered'
) {
throw error
}
user.isAdmin = true
return user.save(function (error) {
if (error != null) {
throw error
}
const ONE_WEEK = 7 * 24 * 60 * 60 // seconds
return OneTimeTokenHandler.getNewToken(
'password',
{
expiresIn: ONE_WEEK,
email: user.email,
user_id: user._id.toString(),
},
function (err, token) {
if (err != null) {
return next(err)
} }
user.isAdmin = true;
return user.save(function(error) {
if (error != null) { throw error; }
const ONE_WEEK = 7 * 24 * 60 * 60; // seconds
return OneTimeTokenHandler.getNewToken("password", { expiresIn: ONE_WEEK, email:user.email, user_id: user._id.toString() }, function(err, token){
if (err != null) { return next(err); }
console.log(""); console.log('')
console.log(`\ console.log(`\
Successfully created ${email} as an admin user. Successfully created ${email} as an admin user.
@ -47,39 +67,50 @@ Please visit the following URL to set a password for ${email} and log in:
${settings.siteUrl}/user/password/set?passwordResetToken=${token} ${settings.siteUrl}/user/password/set?passwordResetToken=${token}
\ \
` `)
); return done()
return done(); }
}); )
}); })
})); }
}); )
)
}
)
return grunt.registerTask('user:delete', "deletes a user and all their data, Usage: grunt user:delete --email joe@example.com", function() { return grunt.registerTask(
const done = this.async(); 'user:delete',
const email = grunt.option("email"); 'deletes a user and all their data, Usage: grunt user:delete --email joe@example.com',
if ((email == null)) { function () {
console.error("Usage: grunt user:delete --email=joe@example.com"); const done = this.async()
process.exit(1); const email = grunt.option('email')
if (email == null) {
console.error('Usage: grunt user:delete --email=joe@example.com')
process.exit(1)
} }
const settings = require("settings-sharelatex"); const settings = require('settings-sharelatex')
const mongodb = require("../web/app/src/infrastructure/mongodb"); const mongodb = require('../web/app/src/infrastructure/mongodb')
const UserGetter = require("../web/app/src/Features/User/UserGetter"); const UserGetter = require('../web/app/src/Features/User/UserGetter')
const UserDeleter = require("../web/app/src/Features/User/UserDeleter"); const UserDeleter = require('../web/app/src/Features/User/UserDeleter')
return mongodb.waitForDb().then(() => UserGetter.getUser({email}, function(error, user) { return mongodb.waitForDb().then(() =>
UserGetter.getUser({ email }, function (error, user) {
if (error != null) { if (error != null) {
throw error; throw error
} }
if ((user == null)) { if (user == null) {
console.log(`user ${email} not in database, potentially already deleted`); console.log(
return done(); `user ${email} not in database, potentially already deleted`
)
return done()
} }
return UserDeleter.deleteUser(user._id, function(err){ return UserDeleter.deleteUser(user._id, function (err) {
if (err != null) { if (err != null) {
throw err; throw err
} }
return done(); return done()
}); })
})); })
}); )
}; }
)
}