mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-13 13:43:41 +01:00
Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3503a7d887 | ||
|
|
77b37b7cf8 | ||
|
|
047268b79d | ||
|
|
527f60316a | ||
|
|
b6eafbcc0a | ||
|
|
ddd2789933 | ||
|
|
7e91256ae3 | ||
|
|
ea2c644695 | ||
|
|
281174eee9 | ||
|
|
de65f2b785 | ||
|
|
9d9fbf06c4 | ||
|
|
39ae15bda0 | ||
|
|
f9d7148b6c | ||
|
|
dbc3bbe9aa | ||
|
|
6495cb3c76 | ||
|
|
76e9829b73 | ||
|
|
8ef6ad5c8e | ||
|
|
bbcdcce617 | ||
|
|
66b2de0750 | ||
|
|
7615dbef54 | ||
|
|
0ff64f51b5 | ||
|
|
4a4957651f | ||
|
|
3414417298 | ||
|
|
cafe99d116 | ||
|
|
3a609993be | ||
|
|
bcf43a4582 | ||
|
|
1ee25c02b1 | ||
|
|
0e3af1c01b | ||
|
|
cd8e28b462 | ||
|
|
035f7c894c | ||
|
|
173bcce718 | ||
|
|
e56d52905b | ||
|
|
d19eb59548 | ||
|
|
7513da4518 | ||
|
|
8c1a06eb94 | ||
|
|
c345c0c940 | ||
|
|
c0651bc13d |
@@ -77,6 +77,7 @@ each service:
|
||||
| `filestore` | 9235 |
|
||||
| `notifications` | 9236 |
|
||||
| `real-time` | 9237 |
|
||||
| `references` | 9238 |
|
||||
| `history-v1` | 9239 |
|
||||
| `project-history` | 9240 |
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ NOTIFICATIONS_HOST=notifications
|
||||
PROJECT_HISTORY_HOST=project-history
|
||||
REALTIME_HOST=real-time
|
||||
REDIS_HOST=redis
|
||||
REFERENCES_HOST=references
|
||||
SESSION_SECRET=foo
|
||||
WEBPACK_HOST=webpack
|
||||
WEB_API_PASSWORD=overleaf
|
||||
|
||||
@@ -112,6 +112,17 @@ services:
|
||||
- ../services/real-time/app.js:/overleaf/services/real-time/app.js
|
||||
- ../services/real-time/config:/overleaf/services/real-time/config
|
||||
|
||||
references:
|
||||
command: ["node", "--watch", "app.js"]
|
||||
environment:
|
||||
- NODE_OPTIONS=--inspect=0.0.0.0:9229
|
||||
ports:
|
||||
- "127.0.0.1:9238:9229"
|
||||
volumes:
|
||||
- ../services/references/app:/overleaf/services/references/app
|
||||
- ../services/references/config:/overleaf/services/references/config
|
||||
- ../services/references/app.js:/overleaf/services/references/app.js
|
||||
|
||||
web:
|
||||
command: ["node", "--watch", "app.js", "--watch-locales"]
|
||||
environment:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
volumes:
|
||||
clsi-cache:
|
||||
clsi-output:
|
||||
filestore-public-files:
|
||||
filestore-template-files:
|
||||
filestore-uploads:
|
||||
@@ -28,13 +29,12 @@ services:
|
||||
- DOCKER_RUNNER=true
|
||||
- TEXLIVE_IMAGE=texlive-full # docker build texlive -t texlive-full
|
||||
- COMPILES_HOST_DIR=${PWD}/compiles
|
||||
- OUTPUT_HOST_DIR=${PWD}/output
|
||||
user: root
|
||||
volumes:
|
||||
- ${PWD}/compiles:/overleaf/services/clsi/compiles
|
||||
- ${PWD}/output:/overleaf/services/clsi/output
|
||||
- ${DOCKER_SOCKET_PATH:-/var/run/docker.sock}:/var/run/docker.sock
|
||||
- clsi-cache:/overleaf/services/clsi/cache
|
||||
- clsi-output:/overleaf/services/clsi/output
|
||||
|
||||
contacts:
|
||||
build:
|
||||
@@ -123,7 +123,7 @@ services:
|
||||
dockerfile: services/real-time/Dockerfile
|
||||
env_file:
|
||||
- dev.env
|
||||
|
||||
|
||||
redis:
|
||||
image: redis:5
|
||||
ports:
|
||||
@@ -131,6 +131,13 @@ services:
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
|
||||
references:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: services/references/Dockerfile
|
||||
env_file:
|
||||
- dev.env
|
||||
|
||||
web:
|
||||
build:
|
||||
context: ..
|
||||
@@ -140,7 +147,7 @@ services:
|
||||
- dev.env
|
||||
environment:
|
||||
- APP_NAME=Overleaf Community Edition
|
||||
- ENABLED_LINKED_FILE_TYPES=project_file,project_output_file
|
||||
- ENABLED_LINKED_FILE_TYPES=project_file,project_output_file,url
|
||||
- EMAIL_CONFIRMATION_DISABLED=true
|
||||
- NODE_ENV=development
|
||||
- OVERLEAF_ALLOW_PUBLIC_ACCESS=true
|
||||
@@ -161,6 +168,7 @@ services:
|
||||
- notifications
|
||||
- project-history
|
||||
- real-time
|
||||
- references
|
||||
|
||||
webpack:
|
||||
build:
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 587 KiB After Width: | Height: | Size: 1.0 MiB |
+4
-12
@@ -32,7 +32,7 @@ services:
|
||||
OVERLEAF_REDIS_HOST: redis
|
||||
REDIS_HOST: redis
|
||||
|
||||
ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file'
|
||||
ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file,url'
|
||||
|
||||
# Enables Thumbnail generation using ImageMagick
|
||||
ENABLE_CONVERSIONS: 'true'
|
||||
@@ -73,19 +73,11 @@ services:
|
||||
## Server Pro ##
|
||||
################
|
||||
|
||||
## The Community Edition is intended for use in environments where all users are trusted and is not appropriate for
|
||||
## scenarios where isolation of users is required. Sandboxed Compiles are not available in the Community Edition,
|
||||
## so the following environment variables must be commented out to avoid compile issues.
|
||||
##
|
||||
## Sandboxed Compiles: https://docs.overleaf.com/on-premises/configuration/overleaf-toolkit/server-pro-only-configuration/sandboxed-compiles
|
||||
## Sandboxed Compiles: https://github.com/overleaf/overleaf/wiki/Server-Pro:-Sandboxed-Compiles
|
||||
SANDBOXED_COMPILES: 'true'
|
||||
### Bind-mount source for /var/lib/overleaf/data/compiles inside the container.
|
||||
SANDBOXED_COMPILES_HOST_DIR_COMPILES: '/home/user/sharelatex_data/data/compiles'
|
||||
### Bind-mount source for /var/lib/overleaf/data/output inside the container.
|
||||
SANDBOXED_COMPILES_HOST_DIR_OUTPUT: '/home/user/sharelatex_data/data/output'
|
||||
### Backwards compatibility (before Server Pro 5.5)
|
||||
DOCKER_RUNNER: 'true'
|
||||
SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true'
|
||||
### Bind-mount source for /var/lib/overleaf/data/compiles inside the container.
|
||||
SANDBOXED_COMPILES_HOST_DIR: '/home/user/sharelatex_data/data/compiles'
|
||||
|
||||
## Works with test LDAP server shown at bottom of docker compose
|
||||
# OVERLEAF_LDAP_URL: 'ldap://ldap:389'
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,46 @@
|
||||
compileFolder
|
||||
|
||||
Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store?
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
|
||||
/node_modules/*
|
||||
data/*/*
|
||||
|
||||
**.swp
|
||||
|
||||
/log.json
|
||||
hash_folder
|
||||
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ access-token-encryptor
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ fetch-utils
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -23,11 +23,11 @@ async function fetchJson(url, opts = {}) {
|
||||
}
|
||||
|
||||
async function fetchJsonWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
fetchOpts.headers = fetchOpts.headers ?? {}
|
||||
fetchOpts.headers.Accept = fetchOpts.headers.Accept ?? 'application/json'
|
||||
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -53,8 +53,8 @@ async function fetchStream(url, opts = {}) {
|
||||
}
|
||||
|
||||
async function fetchStreamWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, abortController, detachSignal } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const { fetchOpts, abortController } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
@@ -76,8 +76,8 @@ async function fetchStreamWithResponse(url, opts = {}) {
|
||||
* @throws {RequestFailedError} if the response has a failure status code
|
||||
*/
|
||||
async function fetchNothing(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -108,9 +108,9 @@ async function fetchRedirect(url, opts = {}) {
|
||||
* @throws {RequestFailedError} if the response has a non redirect status code or missing Location header
|
||||
*/
|
||||
async function fetchRedirectWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
fetchOpts.redirect = 'manual'
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (response.status < 300 || response.status >= 400) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -142,8 +142,8 @@ async function fetchString(url, opts = {}) {
|
||||
}
|
||||
|
||||
async function fetchStringWithResponse(url, opts = {}) {
|
||||
const { fetchOpts, detachSignal } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts, detachSignal)
|
||||
const { fetchOpts } = parseOpts(opts)
|
||||
const response = await performRequest(url, fetchOpts)
|
||||
if (!response.ok) {
|
||||
const body = await maybeGetResponseBody(response)
|
||||
throw new RequestFailedError(url, opts, response, body)
|
||||
@@ -178,14 +178,13 @@ function parseOpts(opts) {
|
||||
|
||||
const abortController = new AbortController()
|
||||
fetchOpts.signal = abortController.signal
|
||||
let detachSignal = () => {}
|
||||
if (opts.signal) {
|
||||
detachSignal = abortOnSignal(abortController, opts.signal)
|
||||
abortOnSignal(abortController, opts.signal)
|
||||
}
|
||||
if (opts.body instanceof Readable) {
|
||||
abortOnDestroyedRequest(abortController, fetchOpts.body)
|
||||
}
|
||||
return { fetchOpts, abortController, detachSignal }
|
||||
return { fetchOpts, abortController }
|
||||
}
|
||||
|
||||
function setupJsonBody(fetchOpts, json) {
|
||||
@@ -209,9 +208,6 @@ function abortOnSignal(abortController, signal) {
|
||||
abortController.abort(signal.reason)
|
||||
}
|
||||
signal.addEventListener('abort', listener)
|
||||
return () => {
|
||||
signal.removeEventListener('abort', listener)
|
||||
}
|
||||
}
|
||||
|
||||
function abortOnDestroyedRequest(abortController, stream) {
|
||||
@@ -230,12 +226,11 @@ function abortOnDestroyedResponse(abortController, response) {
|
||||
})
|
||||
}
|
||||
|
||||
async function performRequest(url, fetchOpts, detachSignal) {
|
||||
async function performRequest(url, fetchOpts) {
|
||||
let response
|
||||
try {
|
||||
response = await fetch(url, fetchOpts)
|
||||
} catch (err) {
|
||||
detachSignal()
|
||||
if (fetchOpts.body instanceof Readable) {
|
||||
fetchOpts.body.destroy()
|
||||
}
|
||||
@@ -244,7 +239,6 @@ async function performRequest(url, fetchOpts, detachSignal) {
|
||||
method: fetchOpts.method ?? 'GET',
|
||||
})
|
||||
}
|
||||
response.body.on('close', detachSignal)
|
||||
if (fetchOpts.body instanceof Readable) {
|
||||
response.body.on('close', () => {
|
||||
if (!fetchOpts.body.readableEnded) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
const { expect } = require('chai')
|
||||
const fs = require('node:fs')
|
||||
const events = require('node:events')
|
||||
const { FetchError, AbortError } = require('node-fetch')
|
||||
const { Readable } = require('node:stream')
|
||||
const { pipeline } = require('node:stream/promises')
|
||||
const { once } = require('node:events')
|
||||
const { TestServer } = require('./helpers/TestServer')
|
||||
const selfsigned = require('selfsigned')
|
||||
@@ -206,31 +203,6 @@ describe('fetch-utils', function () {
|
||||
).to.be.rejectedWith(AbortError)
|
||||
expect(stream.destroyed).to.be.true
|
||||
})
|
||||
|
||||
it('detaches from signal on success', async function () {
|
||||
const signal = AbortSignal.timeout(10_000)
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const s = await fetchStream(this.url('/hello'), { signal })
|
||||
expect(events.getEventListeners(signal, 'abort')).to.have.length(1)
|
||||
await pipeline(s, fs.createWriteStream('/dev/null'))
|
||||
expect(events.getEventListeners(signal, 'abort')).to.have.length(0)
|
||||
}
|
||||
})
|
||||
|
||||
it('detaches from signal on error', async function () {
|
||||
const signal = AbortSignal.timeout(10_000)
|
||||
for (let i = 0; i < 20; i++) {
|
||||
try {
|
||||
await fetchStream(this.url('/500'), { signal })
|
||||
} catch (err) {
|
||||
if (err instanceof RequestFailedError && err.response.status === 500)
|
||||
continue
|
||||
throw err
|
||||
} finally {
|
||||
expect(events.getEventListeners(signal, 'abort')).to.have.length(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('fetchNothing', function () {
|
||||
@@ -419,16 +391,9 @@ async function* infiniteIterator() {
|
||||
async function abortOnceReceived(func, server) {
|
||||
const controller = new AbortController()
|
||||
const promise = func(controller.signal)
|
||||
expect(events.getEventListeners(controller.signal, 'abort')).to.have.length(1)
|
||||
await once(server.events, 'request-received')
|
||||
controller.abort()
|
||||
try {
|
||||
return await promise
|
||||
} finally {
|
||||
expect(events.getEventListeners(controller.signal, 'abort')).to.have.length(
|
||||
0
|
||||
)
|
||||
}
|
||||
return await promise
|
||||
}
|
||||
|
||||
async function expectRequestAborted(req) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ logger
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ metrics
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* before any other module to support code instrumentation.
|
||||
*/
|
||||
|
||||
const metricsModuleImportStartTime = performance.now()
|
||||
|
||||
const APP_NAME = process.env.METRICS_APP_NAME || 'unknown'
|
||||
const BUILD_VERSION = process.env.BUILD_VERSION
|
||||
const ENABLE_PROFILE_AGENT = process.env.ENABLE_PROFILE_AGENT === 'true'
|
||||
@@ -105,5 +103,3 @@ function recordProcessStart() {
|
||||
const metrics = require('.')
|
||||
metrics.inc('process_startup')
|
||||
}
|
||||
|
||||
module.exports = { metricsModuleImportStartTime }
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.1.0",
|
||||
"@google-cloud/profiler": "^6.0.3",
|
||||
"@google-cloud/profiler": "^6.0.0",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.39.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.41.2",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -16,7 +16,6 @@ let VERBOSE_LOGGING
|
||||
let BATCH_RANGE_START
|
||||
let BATCH_RANGE_END
|
||||
let BATCH_MAX_TIME_SPAN_IN_MS
|
||||
let BATCHED_UPDATE_RUNNING = false
|
||||
|
||||
/**
|
||||
* @typedef {import("mongodb").Collection} Collection
|
||||
@@ -35,7 +34,6 @@ let BATCHED_UPDATE_RUNNING = false
|
||||
* @property {string} [BATCH_RANGE_START]
|
||||
* @property {string} [BATCH_SIZE]
|
||||
* @property {string} [VERBOSE_LOGGING]
|
||||
* @property {(progress: string) => Promise<void>} [trackProgress]
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -211,71 +209,59 @@ async function batchedUpdate(
|
||||
update,
|
||||
projection,
|
||||
findOptions,
|
||||
batchedUpdateOptions = {}
|
||||
batchedUpdateOptions
|
||||
) {
|
||||
// only a single batchedUpdate can run at a time due to global variables
|
||||
if (BATCHED_UPDATE_RUNNING) {
|
||||
throw new Error('batchedUpdate is already running')
|
||||
ID_EDGE_PAST = await getIdEdgePast(collection)
|
||||
if (!ID_EDGE_PAST) {
|
||||
console.warn(
|
||||
`The collection ${collection.collectionName} appears to be empty.`
|
||||
)
|
||||
return 0
|
||||
}
|
||||
try {
|
||||
BATCHED_UPDATE_RUNNING = true
|
||||
ID_EDGE_PAST = await getIdEdgePast(collection)
|
||||
if (!ID_EDGE_PAST) {
|
||||
console.warn(
|
||||
`The collection ${collection.collectionName} appears to be empty.`
|
||||
)
|
||||
return 0
|
||||
}
|
||||
refreshGlobalOptionsForBatchedUpdate(batchedUpdateOptions)
|
||||
const { trackProgress = async progress => console.warn(progress) } =
|
||||
batchedUpdateOptions
|
||||
refreshGlobalOptionsForBatchedUpdate(batchedUpdateOptions)
|
||||
|
||||
findOptions = findOptions || {}
|
||||
findOptions.readPreference = READ_PREFERENCE_SECONDARY
|
||||
findOptions = findOptions || {}
|
||||
findOptions.readPreference = READ_PREFERENCE_SECONDARY
|
||||
|
||||
projection = projection || { _id: 1 }
|
||||
let nextBatch
|
||||
let updated = 0
|
||||
let start = BATCH_RANGE_START
|
||||
projection = projection || { _id: 1 }
|
||||
let nextBatch
|
||||
let updated = 0
|
||||
let start = BATCH_RANGE_START
|
||||
|
||||
while (start !== BATCH_RANGE_END) {
|
||||
let end = getNextEnd(start)
|
||||
nextBatch = await getNextBatch(
|
||||
collection,
|
||||
query,
|
||||
start,
|
||||
end,
|
||||
projection,
|
||||
findOptions
|
||||
)
|
||||
if (nextBatch.length > 0) {
|
||||
end = nextBatch[nextBatch.length - 1]._id
|
||||
updated += nextBatch.length
|
||||
while (start !== BATCH_RANGE_END) {
|
||||
let end = getNextEnd(start)
|
||||
nextBatch = await getNextBatch(
|
||||
collection,
|
||||
query,
|
||||
start,
|
||||
end,
|
||||
projection,
|
||||
findOptions
|
||||
)
|
||||
if (nextBatch.length > 0) {
|
||||
end = nextBatch[nextBatch.length - 1]._id
|
||||
updated += nextBatch.length
|
||||
|
||||
if (VERBOSE_LOGGING) {
|
||||
console.log(
|
||||
`Running update on batch with ids ${JSON.stringify(
|
||||
nextBatch.map(entry => entry._id)
|
||||
)}`
|
||||
)
|
||||
}
|
||||
await trackProgress(
|
||||
`Running update on batch ending ${renderObjectId(end)}`
|
||||
if (VERBOSE_LOGGING) {
|
||||
console.log(
|
||||
`Running update on batch with ids ${JSON.stringify(
|
||||
nextBatch.map(entry => entry._id)
|
||||
)}`
|
||||
)
|
||||
|
||||
if (typeof update === 'function') {
|
||||
await update(nextBatch)
|
||||
} else {
|
||||
await performUpdate(collection, nextBatch, update)
|
||||
}
|
||||
} else {
|
||||
console.error(`Running update on batch ending ${renderObjectId(end)}`)
|
||||
}
|
||||
|
||||
if (typeof update === 'function') {
|
||||
await update(nextBatch)
|
||||
} else {
|
||||
await performUpdate(collection, nextBatch, update)
|
||||
}
|
||||
await trackProgress(`Completed batch ending ${renderObjectId(end)}`)
|
||||
start = end
|
||||
}
|
||||
return updated
|
||||
} finally {
|
||||
BATCHED_UPDATE_RUNNING = false
|
||||
console.error(`Completed batch ending ${renderObjectId(end)}`)
|
||||
start = end
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,6 @@ mongo-utils
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,5 @@
|
||||
.nyc_output
|
||||
coverage
|
||||
node_modules/
|
||||
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ o-error
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
+10
-27
@@ -1,34 +1,20 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* Light-weight helpers for handling JavaScript Errors in node.js and the
|
||||
* browser.
|
||||
*/
|
||||
class OError extends Error {
|
||||
/**
|
||||
* The error that is the underlying cause of this error
|
||||
*
|
||||
* @type {unknown}
|
||||
*/
|
||||
cause
|
||||
|
||||
/**
|
||||
* List of errors encountered as the callback chain is unwound
|
||||
*
|
||||
* @type {TaggedError[] | undefined}
|
||||
*/
|
||||
_oErrorTags
|
||||
|
||||
/**
|
||||
* @param {string} message as for built-in Error
|
||||
* @param {Object} [info] extra data to attach to the error
|
||||
* @param {unknown} [cause] the internal error that caused this error
|
||||
* @param {Error} [cause] the internal error that caused this error
|
||||
*/
|
||||
constructor(message, info, cause) {
|
||||
super(message)
|
||||
this.name = this.constructor.name
|
||||
if (info) this.info = info
|
||||
if (cause) this.cause = cause
|
||||
/** @private @type {Array<TaggedError> | undefined} */
|
||||
this._oErrorTags // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +31,7 @@ class OError extends Error {
|
||||
/**
|
||||
* Wrap the given error, which caused this error.
|
||||
*
|
||||
* @param {unknown} cause the internal error that caused this error
|
||||
* @param {Error} cause the internal error that caused this error
|
||||
* @return {this}
|
||||
*/
|
||||
withCause(cause) {
|
||||
@@ -79,16 +65,13 @@ class OError extends Error {
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @template {unknown} E
|
||||
* @param {E} error the error to tag
|
||||
* @param {Error} error the error to tag
|
||||
* @param {string} [message] message with which to tag `error`
|
||||
* @param {Object} [info] extra data with wich to tag `error`
|
||||
* @return {E} the modified `error` argument
|
||||
* @return {Error} the modified `error` argument
|
||||
*/
|
||||
static tag(error, message, info) {
|
||||
const oError = /** @type {{ _oErrorTags: TaggedError[] | undefined }} */ (
|
||||
error
|
||||
)
|
||||
const oError = /** @type{OError} */ (error)
|
||||
|
||||
if (!oError._oErrorTags) oError._oErrorTags = []
|
||||
|
||||
@@ -119,7 +102,7 @@ class OError extends Error {
|
||||
*
|
||||
* If an info property is repeated, the last one wins.
|
||||
*
|
||||
* @param {unknown} error any error (may or may not be an `OError`)
|
||||
* @param {Error | null | undefined} error any error (may or may not be an `OError`)
|
||||
* @return {Object}
|
||||
*/
|
||||
static getFullInfo(error) {
|
||||
@@ -146,7 +129,7 @@ class OError extends Error {
|
||||
* Return the `stack` property from `error`, including the `stack`s for any
|
||||
* tagged errors added with `OError.tag` and for any `cause`s.
|
||||
*
|
||||
* @param {unknown} error any error (may or may not be an `OError`)
|
||||
* @param {Error | null | undefined} error any error (may or may not be an `OError`)
|
||||
* @return {string}
|
||||
*/
|
||||
static getFullStack(error) {
|
||||
@@ -160,7 +143,7 @@ class OError extends Error {
|
||||
stack += `\n${oError._oErrorTags.map(tag => tag.stack).join('\n')}`
|
||||
}
|
||||
|
||||
const causeStack = OError.getFullStack(oError.cause)
|
||||
const causeStack = oError.cause && OError.getFullStack(oError.cause)
|
||||
if (causeStack) {
|
||||
stack += '\ncaused by:\n' + indent(causeStack)
|
||||
}
|
||||
|
||||
@@ -268,11 +268,6 @@ describe('utils', function () {
|
||||
expect(OError.getFullInfo(null)).to.deep.equal({})
|
||||
})
|
||||
|
||||
it('works when given a string', function () {
|
||||
const err = 'not an error instance'
|
||||
expect(OError.getFullInfo(err)).to.deep.equal({})
|
||||
})
|
||||
|
||||
it('works on a normal error', function () {
|
||||
const err = new Error('foo')
|
||||
expect(OError.getFullInfo(err)).to.deep.equal({})
|
||||
|
||||
@@ -35,14 +35,6 @@ describe('OError', function () {
|
||||
expect(err2.cause.message).to.equal('cause 2')
|
||||
})
|
||||
|
||||
it('accepts non-Error causes', function () {
|
||||
const err1 = new OError('foo', {}, 'not-an-error')
|
||||
expect(err1.cause).to.equal('not-an-error')
|
||||
|
||||
const err2 = new OError('foo').withCause('not-an-error')
|
||||
expect(err2.cause).to.equal('not-an-error')
|
||||
})
|
||||
|
||||
it('handles a custom error type with a cause', function () {
|
||||
function doSomethingBadInternally() {
|
||||
throw new Error('internal error')
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,4 @@
|
||||
/node_modules
|
||||
*.swp
|
||||
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ object-persistor
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -305,10 +305,8 @@ module.exports = class FSPersistor extends AbstractPersistor {
|
||||
|
||||
async _listDirectory(path) {
|
||||
if (this.useSubdirectories) {
|
||||
// eslint-disable-next-line @typescript-eslint/return-await
|
||||
return await glob(Path.join(path, '**'))
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/return-await
|
||||
return await glob(`${path}_*`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,5 @@
|
||||
/coverage
|
||||
/node_modules
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ overleaf-editor-core
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -18,7 +18,6 @@ const MoveFileOperation = require('./lib/operation/move_file_operation')
|
||||
const SetCommentStateOperation = require('./lib/operation/set_comment_state_operation')
|
||||
const EditFileOperation = require('./lib/operation/edit_file_operation')
|
||||
const EditNoOperation = require('./lib/operation/edit_no_operation')
|
||||
const EditOperationTransformer = require('./lib/operation/edit_operation_transformer')
|
||||
const SetFileMetadataOperation = require('./lib/operation/set_file_metadata_operation')
|
||||
const NoOperation = require('./lib/operation/no_operation')
|
||||
const Operation = require('./lib/operation')
|
||||
@@ -44,8 +43,6 @@ const TrackingProps = require('./lib/file_data/tracking_props')
|
||||
const Range = require('./lib/range')
|
||||
const CommentList = require('./lib/file_data/comment_list')
|
||||
const LazyStringFileData = require('./lib/file_data/lazy_string_file_data')
|
||||
const StringFileData = require('./lib/file_data/string_file_data')
|
||||
const EditOperationBuilder = require('./lib/operation/edit_operation_builder')
|
||||
|
||||
exports.AddCommentOperation = AddCommentOperation
|
||||
exports.Author = Author
|
||||
@@ -61,7 +58,6 @@ exports.DeleteCommentOperation = DeleteCommentOperation
|
||||
exports.File = File
|
||||
exports.FileMap = FileMap
|
||||
exports.LazyStringFileData = LazyStringFileData
|
||||
exports.StringFileData = StringFileData
|
||||
exports.History = History
|
||||
exports.Label = Label
|
||||
exports.AddFileOperation = AddFileOperation
|
||||
@@ -69,8 +65,6 @@ exports.MoveFileOperation = MoveFileOperation
|
||||
exports.SetCommentStateOperation = SetCommentStateOperation
|
||||
exports.EditFileOperation = EditFileOperation
|
||||
exports.EditNoOperation = EditNoOperation
|
||||
exports.EditOperationBuilder = EditOperationBuilder
|
||||
exports.EditOperationTransformer = EditOperationTransformer
|
||||
exports.SetFileMetadataOperation = SetFileMetadataOperation
|
||||
exports.NoOperation = NoOperation
|
||||
exports.Operation = Operation
|
||||
|
||||
@@ -13,7 +13,7 @@ const V2DocVersions = require('./v2_doc_versions')
|
||||
|
||||
/**
|
||||
* @import Author from "./author"
|
||||
* @import { BlobStore, RawChange } from "./types"
|
||||
* @import { BlobStore } from "./types"
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ class Change {
|
||||
/**
|
||||
* For serialization.
|
||||
*
|
||||
* @return {RawChange}
|
||||
* @return {Object}
|
||||
*/
|
||||
toRaw() {
|
||||
function toRaw(object) {
|
||||
|
||||
@@ -88,14 +88,6 @@ class StringFileData extends FileData {
|
||||
return content
|
||||
}
|
||||
|
||||
/**
|
||||
* Return docstore view of a doc: each line separated
|
||||
* @return {string[]}
|
||||
*/
|
||||
getLines() {
|
||||
return this.getContent({ filterTrackedDeletes: true }).split('\n')
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
getByteLength() {
|
||||
return Buffer.byteLength(this.content)
|
||||
|
||||
@@ -36,20 +36,6 @@ class EditOperationBuilder {
|
||||
}
|
||||
throw new Error('Unsupported operation in EditOperationBuilder.fromJSON')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} raw
|
||||
* @return {raw is RawEditOperation}
|
||||
*/
|
||||
static isValid(raw) {
|
||||
return (
|
||||
isTextOperation(raw) ||
|
||||
isRawAddCommentOperation(raw) ||
|
||||
isRawDeleteCommentOperation(raw) ||
|
||||
isRawSetCommentStateOperation(raw) ||
|
||||
isRawEditNoOperation(raw)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ promise-utils
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,13 @@
|
||||
**.swp
|
||||
|
||||
app.js
|
||||
app/js/
|
||||
test/unit/js/
|
||||
public/build/
|
||||
|
||||
node_modules/
|
||||
|
||||
/public/js/chat.js
|
||||
plato/
|
||||
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ ranges-tracker
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,13 @@
|
||||
**.swp
|
||||
|
||||
app.js
|
||||
app/js/
|
||||
test/unit/js/
|
||||
public/build/
|
||||
|
||||
node_modules/
|
||||
|
||||
/public/js/chat.js
|
||||
plato/
|
||||
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -97,8 +97,7 @@ module.exports = class RedisLocker {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {function(Error, boolean, string): void} callback
|
||||
* @param {Callback} callback
|
||||
*/
|
||||
tryLock(id, callback) {
|
||||
if (callback == null) {
|
||||
@@ -107,7 +106,7 @@ module.exports = class RedisLocker {
|
||||
const lockValue = this.randomLock()
|
||||
const key = this.getKey(id)
|
||||
const startTime = Date.now()
|
||||
this.rclient.set(
|
||||
return this.rclient.set(
|
||||
key,
|
||||
lockValue,
|
||||
'EX',
|
||||
@@ -122,7 +121,7 @@ module.exports = class RedisLocker {
|
||||
const timeTaken = Date.now() - startTime
|
||||
if (timeTaken > MAX_REDIS_REQUEST_LENGTH) {
|
||||
// took too long, so try to free the lock
|
||||
this.releaseLock(id, lockValue, function (err, result) {
|
||||
return this.releaseLock(id, lockValue, function (err, result) {
|
||||
if (err != null) {
|
||||
return callback(err)
|
||||
} // error freeing lock
|
||||
@@ -140,8 +139,7 @@ module.exports = class RedisLocker {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {function(Error, string): void} callback
|
||||
* @param {Callback} callback
|
||||
*/
|
||||
getLock(id, callback) {
|
||||
if (callback == null) {
|
||||
@@ -155,7 +153,7 @@ module.exports = class RedisLocker {
|
||||
return callback(e)
|
||||
}
|
||||
|
||||
this.tryLock(id, (error, gotLock, lockValue) => {
|
||||
return this.tryLock(id, (error, gotLock, lockValue) => {
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
@@ -175,15 +173,14 @@ module.exports = class RedisLocker {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {function(Error, boolean): void} callback
|
||||
* @param {Callback} callback
|
||||
*/
|
||||
checkLock(id, callback) {
|
||||
if (callback == null) {
|
||||
callback = function () {}
|
||||
}
|
||||
const key = this.getKey(id)
|
||||
this.rclient.exists(key, (err, exists) => {
|
||||
return this.rclient.exists(key, (err, exists) => {
|
||||
if (err != null) {
|
||||
return callback(err)
|
||||
}
|
||||
@@ -199,26 +196,30 @@ module.exports = class RedisLocker {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {string} lockValue
|
||||
* @param {function(Error, boolean): void} callback
|
||||
* @param {Callback} callback
|
||||
*/
|
||||
releaseLock(id, lockValue, callback) {
|
||||
const key = this.getKey(id)
|
||||
this.rclient.eval(UNLOCK_SCRIPT, 1, key, lockValue, (err, result) => {
|
||||
if (err != null) {
|
||||
return callback(err)
|
||||
} else if (result != null && result !== 1) {
|
||||
// successful unlock should release exactly one key
|
||||
logger.error(
|
||||
{ id, key, lockValue, redis_err: err, redis_result: result },
|
||||
'unlocking error'
|
||||
)
|
||||
metrics.inc(this.metricsPrefix + '-unlock-error')
|
||||
return callback(new Error('tried to release timed out lock'))
|
||||
} else {
|
||||
return callback(null, result)
|
||||
return this.rclient.eval(
|
||||
UNLOCK_SCRIPT,
|
||||
1,
|
||||
key,
|
||||
lockValue,
|
||||
(err, result) => {
|
||||
if (err != null) {
|
||||
return callback(err)
|
||||
} else if (result != null && result !== 1) {
|
||||
// successful unlock should release exactly one key
|
||||
logger.error(
|
||||
{ id, key, lockValue, redis_err: err, redis_result: result },
|
||||
'unlocking error'
|
||||
)
|
||||
metrics.inc(this.metricsPrefix + '-unlock-error')
|
||||
return callback(new Error('tried to release timed out lock'))
|
||||
} else {
|
||||
return callback(null, result)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,6 @@ redis-wrapper
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,5 @@
|
||||
/.npmrc
|
||||
/node_modules
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ settings
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# managed by monorepo$ bin/update_build_scripts
|
||||
.npmrc
|
||||
@@ -1 +1 @@
|
||||
22.15.1
|
||||
20.18.2
|
||||
|
||||
@@ -5,6 +5,6 @@ stream-utils
|
||||
--env-pass-through=
|
||||
--esmock-loader=False
|
||||
--is-library=True
|
||||
--node-version=22.15.1
|
||||
--node-version=20.18.2
|
||||
--public-repo=False
|
||||
--script-version=4.7.0
|
||||
--script-version=4.5.0
|
||||
|
||||
Generated
+3445
-3596
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -37,7 +37,7 @@
|
||||
},
|
||||
"swagger-tools": {
|
||||
"body-parser": "1.20.3",
|
||||
"multer": "2.0.0",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"path-to-regexp": "3.3.0",
|
||||
"qs": "6.13.0"
|
||||
}
|
||||
@@ -55,7 +55,6 @@
|
||||
"services/analytics",
|
||||
"services/chat",
|
||||
"services/clsi",
|
||||
"services/clsi-cache",
|
||||
"services/clsi-perf",
|
||||
"services/contacts",
|
||||
"services/docstore",
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
diff --git a/node_modules/@node-saml/node-saml/lib/saml.js b/node_modules/@node-saml/node-saml/lib/saml.js
|
||||
index fba15b9..a5778cb 100644
|
||||
--- a/node_modules/@node-saml/node-saml/lib/saml.js
|
||||
+++ b/node_modules/@node-saml/node-saml/lib/saml.js
|
||||
@@ -336,7 +336,8 @@ class SAML {
|
||||
const requestOrResponse = request || response;
|
||||
(0, utility_1.assertRequired)(requestOrResponse, "either request or response is required");
|
||||
let buffer;
|
||||
- if (this.options.skipRequestCompression) {
|
||||
+ // logout requestOrResponse must be compressed anyway
|
||||
+ if (this.options.skipRequestCompression && operation !== "logout") {
|
||||
buffer = Buffer.from(requestOrResponse, "utf8");
|
||||
}
|
||||
else {
|
||||
@@ -495,7 +496,7 @@ class SAML {
|
||||
try {
|
||||
xml = Buffer.from(container.SAMLResponse, "base64").toString("utf8");
|
||||
doc = await (0, xml_1.parseDomFromString)(xml);
|
||||
- const inResponseToNodes = xml_1.xpath.selectAttributes(doc, "/*[local-name()='Response']/@InResponseTo");
|
||||
+ const inResponseToNodes = xml_1.xpath.selectAttributes(doc, "/*[local-name()='Response' or local-name()='LogoutResponse']/@InResponseTo");
|
||||
if (inResponseToNodes) {
|
||||
inResponseTo = inResponseToNodes.length ? inResponseToNodes[0].nodeValue : null;
|
||||
await this.validateInResponseTo(inResponseTo);
|
||||
@@ -0,0 +1,64 @@
|
||||
diff --git a/node_modules/ldapauth-fork/lib/ldapauth.js b/node_modules/ldapauth-fork/lib/ldapauth.js
|
||||
index 85ecf36a8b..a7d07e0f78 100644
|
||||
--- a/node_modules/ldapauth-fork/lib/ldapauth.js
|
||||
+++ b/node_modules/ldapauth-fork/lib/ldapauth.js
|
||||
@@ -69,6 +69,7 @@ function LdapAuth(opts) {
|
||||
this.opts.bindProperty || (this.opts.bindProperty = 'dn');
|
||||
this.opts.groupSearchScope || (this.opts.groupSearchScope = 'sub');
|
||||
this.opts.groupDnProperty || (this.opts.groupDnProperty = 'dn');
|
||||
+ this.opts.tlsStarted = false;
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
@@ -108,21 +109,7 @@ function LdapAuth(opts) {
|
||||
this._userClient.on('error', this._handleError.bind(this));
|
||||
|
||||
var self = this;
|
||||
- if (this.opts.starttls) {
|
||||
- // When starttls is enabled, this callback supplants the 'connect' callback
|
||||
- this._adminClient.starttls(this.opts.tlsOptions, this._adminClient.controls, function(err) {
|
||||
- if (err) {
|
||||
- self._handleError(err);
|
||||
- } else {
|
||||
- self._onConnectAdmin();
|
||||
- }
|
||||
- });
|
||||
- this._userClient.starttls(this.opts.tlsOptions, this._userClient.controls, function(err) {
|
||||
- if (err) {
|
||||
- self._handleError(err);
|
||||
- }
|
||||
- });
|
||||
- } else if (opts.reconnect) {
|
||||
+ if (opts.reconnect && !this.opts.starttls) {
|
||||
this.once('_installReconnectListener', function() {
|
||||
self.log && self.log.trace('install reconnect listener');
|
||||
self._adminClient.on('connect', function() {
|
||||
@@ -384,6 +371,28 @@ LdapAuth.prototype._findGroups = function(user, callback) {
|
||||
*/
|
||||
LdapAuth.prototype.authenticate = function(username, password, callback) {
|
||||
var self = this;
|
||||
+ if (this.opts.starttls && !this.opts.tlsStarted) {
|
||||
+ // When starttls is enabled, this callback supplants the 'connect' callback
|
||||
+ this._adminClient.starttls(this.opts.tlsOptions, this._adminClient.controls, function (err) {
|
||||
+ if (err) {
|
||||
+ self._handleError(err);
|
||||
+ } else {
|
||||
+ self._onConnectAdmin(function(){self._handleAuthenticate(username, password, callback);});
|
||||
+ }
|
||||
+ });
|
||||
+ this._userClient.starttls(this.opts.tlsOptions, this._userClient.controls, function (err) {
|
||||
+ if (err) {
|
||||
+ self._handleError(err);
|
||||
+ }
|
||||
+ });
|
||||
+ } else {
|
||||
+ self._handleAuthenticate(username, password, callback);
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+LdapAuth.prototype._handleAuthenticate = function (username, password, callback) {
|
||||
+ this.opts.tlsStarted = true;
|
||||
+ var self = this;
|
||||
|
||||
if (typeof password === 'undefined' || password === null || password === '') {
|
||||
return callback(new Error('no password given'));
|
||||
@@ -1,22 +0,0 @@
|
||||
diff --git a/node_modules/pdfjs-dist/build/pdf.worker.mjs b/node_modules/pdfjs-dist/build/pdf.worker.mjs
|
||||
index 6c5c6f1..bb6b7d1 100644
|
||||
--- a/node_modules/pdfjs-dist/build/pdf.worker.mjs
|
||||
+++ b/node_modules/pdfjs-dist/build/pdf.worker.mjs
|
||||
@@ -1830,7 +1830,7 @@ async function __wbg_init(module_or_path) {
|
||||
}
|
||||
}
|
||||
if (typeof module_or_path === 'undefined') {
|
||||
- module_or_path = new URL('qcms_bg.wasm', import.meta.url);
|
||||
+ module_or_path = new URL(/* webpackIgnore: true */ 'qcms_bg.wasm', import.meta.url);
|
||||
}
|
||||
const imports = __wbg_get_imports();
|
||||
if (typeof module_or_path === 'string' || typeof Request === 'function' && module_or_path instanceof Request || typeof URL === 'function' && module_or_path instanceof URL) {
|
||||
@@ -5358,7 +5358,7 @@ var OpenJPEG = (() => {
|
||||
if (Module["locateFile"]) {
|
||||
return locateFile("openjpeg.wasm");
|
||||
}
|
||||
- return new URL("openjpeg.wasm", import.meta.url).href;
|
||||
+ return new URL(/* webpackIgnore: true */ "openjpeg.wasm", import.meta.url).href;
|
||||
}
|
||||
function getBinarySync(file) {
|
||||
if (file == wasmBinaryFile && wasmBinary) {
|
||||
@@ -2,7 +2,7 @@
|
||||
# Overleaf Base Image (sharelatex/sharelatex-base)
|
||||
# --------------------------------------------------
|
||||
|
||||
FROM phusion/baseimage:noble-1.0.2
|
||||
FROM phusion/baseimage:noble-1.0.0
|
||||
|
||||
# Makes sure LuaTex cache is writable
|
||||
# -----------------------------------
|
||||
@@ -10,7 +10,7 @@ ENV TEXMFVAR=/var/lib/overleaf/tmp/texmf-var
|
||||
|
||||
# Update to ensure dependencies are updated
|
||||
# ------------------------------------------
|
||||
ENV REBUILT_AFTER="2025-05-19"
|
||||
ENV REBUILT_AFTER="2025-03-27"
|
||||
|
||||
# Install dependencies
|
||||
# --------------------
|
||||
@@ -30,7 +30,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
# install Node.js https://github.com/nodesource/distributions#nodejs
|
||||
&& mkdir -p /etc/apt/keyrings \
|
||||
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
||||
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
|
||||
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y nodejs \
|
||||
\
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
*/20 * * * * root /overleaf/cron/project-history-periodic-flush.sh >> /var/log/overleaf/cron-project-history-periodic-flush.log 2>&1
|
||||
30 * * * * root /overleaf/cron/project-history-retry-soft.sh >> /var/log/overleaf/project-history-retry-soft.log 2>&1
|
||||
45 * * * * root /overleaf/cron/project-history-retry-hard.sh >> /var/log/overleaf/project-history-retry-hard.log 2>&1
|
||||
0 3 * * * root /overleaf/cron/project-history-flush-all.sh >> /var/log/overleaf/project-history-flush-all.log 2>&1
|
||||
|
||||
@@ -45,17 +45,5 @@
|
||||
"clusterWorkers": "CLUSTER_WORKERS",
|
||||
"maxFileUploadSize": "MAX_FILE_UPLOAD_SIZE",
|
||||
"httpsOnly": "HTTPS_ONLY",
|
||||
"httpRequestTimeout": "OVERLEAF_HISTORY_V1_HTTP_REQUEST_TIMEOUT",
|
||||
"redis": {
|
||||
"history": {
|
||||
"host": "OVERLEAF_REDIS_HOST",
|
||||
"password": "OVERLEAF_REDIS_PASS",
|
||||
"port": "OVERLEAF_REDIS_PORT"
|
||||
},
|
||||
"lock": {
|
||||
"host": "OVERLEAF_REDIS_HOST",
|
||||
"password": "OVERLEAF_REDIS_PASS",
|
||||
"port": "OVERLEAF_REDIS_PORT"
|
||||
}
|
||||
}
|
||||
"httpRequestTimeout": "OVERLEAF_HISTORY_V1_HTTP_REQUEST_TIMEOUT"
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ export HISTORY_V1_HOST=127.0.0.1
|
||||
export NOTIFICATIONS_HOST=127.0.0.1
|
||||
export PROJECT_HISTORY_HOST=127.0.0.1
|
||||
export REALTIME_HOST=127.0.0.1
|
||||
export REFERENCES_HOST=127.0.0.1
|
||||
export WEB_HOST=127.0.0.1
|
||||
export WEB_API_HOST=127.0.0.1
|
||||
|
||||
@@ -79,7 +79,6 @@ const settings = {
|
||||
host: process.env.OVERLEAF_REDIS_HOST || 'dockerhost',
|
||||
port: process.env.OVERLEAF_REDIS_PORT || '6379',
|
||||
password: process.env.OVERLEAF_REDIS_PASS || undefined,
|
||||
tls: process.env.OVERLEAF_REDIS_TLS === 'true' ? {} : undefined,
|
||||
key_schema: {
|
||||
// document-updater
|
||||
blockingKey({ doc_id }) {
|
||||
@@ -140,7 +139,6 @@ const settings = {
|
||||
api: redisConfig,
|
||||
pubsub: redisConfig,
|
||||
project_history: redisConfig,
|
||||
references: redisConfig,
|
||||
|
||||
project_history_migration: {
|
||||
host: redisConfig.host,
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
|
||||
echo "---------------------------------"
|
||||
echo "Flush all project-history changes"
|
||||
echo "---------------------------------"
|
||||
date
|
||||
|
||||
source /etc/container_environment.sh
|
||||
source /etc/overleaf/env.sh
|
||||
cd /overleaf/services/project-history && node scripts/flush_all.js
|
||||
|
||||
echo "Done flushing all project-history changes"
|
||||
@@ -1,14 +0,0 @@
|
||||
FROM sharelatex/sharelatex:5.4.0
|
||||
|
||||
RUN apt update && apt install -y linux-libc-dev \
|
||||
&& unattended-upgrade --verbose --no-minimal-upgrade-steps \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY package-lock.json.diff .
|
||||
RUN patch package-lock.json < package-lock.json.diff
|
||||
RUN npm install --omit=dev
|
||||
|
||||
|
||||
# fix tls configuration in redis
|
||||
COPY issue_24996.patch .
|
||||
RUN patch -p0 /etc/overleaf/settings.js < issue_24996.patch \
|
||||
&& rm issue_24996.patch
|
||||
@@ -1,10 +0,0 @@
|
||||
--- settings.js
|
||||
+++ settings.js
|
||||
@@ -79,6 +79,7 @@ const settings = {
|
||||
host: process.env.OVERLEAF_REDIS_HOST || 'dockerhost',
|
||||
port: process.env.OVERLEAF_REDIS_PORT || '6379',
|
||||
password: process.env.OVERLEAF_REDIS_PASS || undefined,
|
||||
+ tls: process.env.OVERLEAF_REDIS_TLS === 'true' ? {} : undefined,
|
||||
key_schema: {
|
||||
// document-updater
|
||||
blockingKey({ doc_id }) {
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
NODE_PARAMS=""
|
||||
if [ "$DEBUG_NODE" == "true" ]; then
|
||||
echo "running debug - references"
|
||||
NODE_PARAMS="--inspect=0.0.0.0:30560"
|
||||
fi
|
||||
|
||||
source /etc/overleaf/env.sh
|
||||
export LISTEN_ADDRESS=127.0.0.1
|
||||
|
||||
exec /sbin/setuser www-data /usr/bin/node $NODE_PARAMS /overleaf/services/references/app.js >> /var/log/overleaf/references.log 2>&1
|
||||
@@ -29,6 +29,9 @@ module.exports = [
|
||||
{
|
||||
name: 'project-history',
|
||||
},
|
||||
{
|
||||
name: 'references',
|
||||
},
|
||||
{
|
||||
name: 'history-v1',
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:22.15.1
|
||||
FROM node:20.18.2
|
||||
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \
|
||||
&& echo \
|
||||
"deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('Accounts', function () {
|
||||
it('can log in and out', function () {
|
||||
login('user@example.com')
|
||||
cy.visit('/project')
|
||||
cy.findByRole('menuitem', { name: 'Account' }).click()
|
||||
cy.findByText('Account').click()
|
||||
cy.findByText('Log Out').click()
|
||||
cy.url().should('include', '/login')
|
||||
cy.visit('/project')
|
||||
|
||||
@@ -293,8 +293,8 @@ describe('admin panel', function () {
|
||||
cy.findByText(deletedProjectName).should('not.exist')
|
||||
|
||||
cy.log('navigate to thrashed projects and delete the project')
|
||||
cy.get('.project-list-sidebar-scroll').within(() => {
|
||||
cy.findByText('Trashed projects').click()
|
||||
cy.get('.project-list-sidebar-react').within(() => {
|
||||
cy.findByText('Trashed Projects').click()
|
||||
})
|
||||
findProjectRow(deletedProjectName).within(() =>
|
||||
cy.findByRole('button', { name: 'Delete' }).click()
|
||||
@@ -318,8 +318,8 @@ describe('admin panel', function () {
|
||||
cy.log('login as the user and verify the project is restored')
|
||||
login(user1)
|
||||
cy.visit('/project')
|
||||
cy.get('.project-list-sidebar-scroll').within(() => {
|
||||
cy.findByText('Trashed projects').click()
|
||||
cy.get('.project-list-sidebar-react').within(() => {
|
||||
cy.findByText('Trashed Projects').click()
|
||||
})
|
||||
cy.findByText(`${deletedProjectName} (Restored)`)
|
||||
})
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('Project creation and compilation', function () {
|
||||
login('user@example.com')
|
||||
|
||||
createProject(sourceProjectName, {
|
||||
type: 'Example project',
|
||||
type: 'Example Project',
|
||||
open: false,
|
||||
}).as('sourceProjectId')
|
||||
createProject(targetProjectName)
|
||||
@@ -79,7 +79,7 @@ describe('Project creation and compilation', function () {
|
||||
const targetProjectName = `${sourceProjectName}-target`
|
||||
login('user@example.com')
|
||||
createProject(sourceProjectName, {
|
||||
type: 'Example project',
|
||||
type: 'Example Project',
|
||||
open: false,
|
||||
}).as('sourceProjectId')
|
||||
createProject(targetProjectName).as('targetProjectId')
|
||||
@@ -102,6 +102,10 @@ describe('Project creation and compilation', function () {
|
||||
cy.findByText('Invite not yet accepted.')
|
||||
})
|
||||
|
||||
cy.visit('/project')
|
||||
cy.findByText('Account').click()
|
||||
cy.findByText('Log Out').click()
|
||||
|
||||
login('collaborator@example.com')
|
||||
openProjectViaInviteNotification(targetProjectName)
|
||||
cy.get('@targetProjectId').then(targetProjectId => {
|
||||
|
||||
@@ -20,7 +20,7 @@ services:
|
||||
OVERLEAF_EMAIL_SMTP_HOST: 'mailtrap'
|
||||
OVERLEAF_EMAIL_SMTP_PORT: '25'
|
||||
OVERLEAF_EMAIL_SMTP_IGNORE_TLS: 'true'
|
||||
ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file'
|
||||
ENABLED_LINKED_FILE_TYPES: 'project_file,project_output_file,url'
|
||||
ENABLE_CONVERSIONS: 'true'
|
||||
EMAIL_CONFIRMATION_DISABLED: 'true'
|
||||
healthcheck:
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import {
|
||||
createNewFile,
|
||||
createProject,
|
||||
enableLinkSharing,
|
||||
openFile,
|
||||
openProjectById,
|
||||
openProjectViaLinkSharingAsUser,
|
||||
toggleTrackChanges,
|
||||
} from './helpers/project'
|
||||
import { isExcludedBySharding, startWith } from './helpers/config'
|
||||
import { ensureUserExists, login } from './helpers/login'
|
||||
@@ -22,7 +26,7 @@ describe('editor', () => {
|
||||
beforeWithReRunOnTestRetry(function () {
|
||||
projectName = `project-${uuid()}`
|
||||
login('user@example.com')
|
||||
createProject(projectName, { type: 'Example project', open: false }).then(
|
||||
createProject(projectName, { type: 'Example Project', open: false }).then(
|
||||
id => (projectId = id)
|
||||
)
|
||||
;({ recompile, waitForCompileRateLimitCoolOff } =
|
||||
@@ -62,12 +66,12 @@ describe('editor', () => {
|
||||
|
||||
cy.log('add word to dictionary')
|
||||
cy.get('.ol-cm-spelling-error').contains(word).rightclick()
|
||||
cy.findByText('Add to dictionary').click()
|
||||
cy.findByText('Add to Dictionary').click()
|
||||
cy.get('.ol-cm-spelling-error').should('not.exist')
|
||||
|
||||
cy.log('remove word from dictionary')
|
||||
cy.get('button').contains('Menu').click()
|
||||
cy.get('button#dictionary-settings').contains('Edit').click()
|
||||
cy.get('button').contains('Edit').click()
|
||||
cy.get('[id="dictionary-modal"]').within(() => {
|
||||
cy.findByText(word)
|
||||
.parent()
|
||||
@@ -88,6 +92,136 @@ describe('editor', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('collaboration', () => {
|
||||
beforeWithReRunOnTestRetry(function () {
|
||||
enableLinkSharing().then(({ linkSharingReadAndWrite }) => {
|
||||
const email = 'collaborator@example.com'
|
||||
login(email)
|
||||
openProjectViaLinkSharingAsUser(
|
||||
linkSharingReadAndWrite,
|
||||
projectName,
|
||||
email
|
||||
)
|
||||
})
|
||||
|
||||
login('user@example.com')
|
||||
waitForCompileRateLimitCoolOff(() => {
|
||||
openProjectById(projectId)
|
||||
})
|
||||
})
|
||||
|
||||
it('track-changes', () => {
|
||||
cy.log('disable track-changes before populating doc')
|
||||
toggleTrackChanges(false)
|
||||
|
||||
const fileName = createNewFile()
|
||||
const oldContent = 'oldContent'
|
||||
cy.get('.cm-line').type(`${oldContent}\n\nstatic`)
|
||||
|
||||
cy.log('recompile to force flush')
|
||||
recompile()
|
||||
|
||||
cy.log('enable track-changes for everyone')
|
||||
toggleTrackChanges(true)
|
||||
|
||||
login('collaborator@example.com')
|
||||
waitForCompileRateLimitCoolOff(() => {
|
||||
openProjectById(projectId)
|
||||
})
|
||||
openFile(fileName, 'static')
|
||||
|
||||
cy.log('make changes in main file')
|
||||
// cy.type() "clicks" in the center of the selected element before typing. This "click" discards the text as selected by the dblclick.
|
||||
// Go down to the lower level event based typing, the frontend tests in web use similar events.
|
||||
cy.get('.cm-editor').as('editor')
|
||||
cy.get('@editor').findByText(oldContent).dblclick()
|
||||
cy.get('@editor').trigger('keydown', { key: 'Delete' })
|
||||
cy.get('@editor').trigger('keydown', { key: 'Enter' })
|
||||
cy.get('@editor').trigger('keydown', { key: 'Enter' })
|
||||
|
||||
cy.log('recompile to force flush')
|
||||
recompile()
|
||||
|
||||
login('user@example.com')
|
||||
waitForCompileRateLimitCoolOff(() => {
|
||||
openProjectById(projectId)
|
||||
})
|
||||
openFile(fileName, 'static')
|
||||
|
||||
cy.log('reject changes')
|
||||
cy.contains('.toolbar-item', 'Review').click()
|
||||
cy.get('.cm-content').should('not.contain.text', oldContent)
|
||||
cy.findByText('Reject change').click({ force: true })
|
||||
cy.contains('.toolbar-item', 'Review').click()
|
||||
|
||||
cy.log('recompile to force flush')
|
||||
recompile()
|
||||
|
||||
cy.log('verify the changes are applied')
|
||||
cy.get('.cm-content').should('contain.text', oldContent)
|
||||
|
||||
cy.log('disable track-changes for everyone again')
|
||||
toggleTrackChanges(false)
|
||||
})
|
||||
|
||||
it('track-changes rich text', () => {
|
||||
cy.log('disable track-changes before populating doc')
|
||||
toggleTrackChanges(false)
|
||||
|
||||
const fileName = createNewFile()
|
||||
const oldContent = 'oldContent'
|
||||
cy.get('.cm-line').type(`\\section{{}${oldContent}}\n\nstatic`)
|
||||
|
||||
cy.log('recompile to force flush')
|
||||
recompile()
|
||||
|
||||
cy.log('enable track-changes for everyone')
|
||||
toggleTrackChanges(true)
|
||||
|
||||
login('collaborator@example.com')
|
||||
waitForCompileRateLimitCoolOff(() => {
|
||||
openProjectById(projectId)
|
||||
})
|
||||
cy.log('enable visual editor and make changes in main file')
|
||||
cy.findByText('Visual Editor').click()
|
||||
|
||||
openFile(fileName, 'static')
|
||||
|
||||
// cy.type() "clicks" in the center of the selected element before typing. This "click" discards the text as selected by the dblclick.
|
||||
// Go down to the lower level event based typing, the frontend tests in web use similar events.
|
||||
cy.get('.cm-editor').as('editor')
|
||||
cy.get('@editor').findByText(oldContent).dblclick()
|
||||
cy.get('@editor').trigger('keydown', { key: 'Delete' })
|
||||
cy.get('@editor').trigger('keydown', { key: 'Enter' })
|
||||
cy.get('@editor').trigger('keydown', { key: 'Enter' })
|
||||
|
||||
cy.log('recompile to force flush')
|
||||
recompile()
|
||||
|
||||
login('user@example.com')
|
||||
waitForCompileRateLimitCoolOff(() => {
|
||||
openProjectById(projectId)
|
||||
})
|
||||
openFile(fileName, 'static')
|
||||
|
||||
cy.log('reject changes')
|
||||
cy.contains('.toolbar-item', 'Review').click()
|
||||
cy.get('.cm-content').should('not.contain.text', oldContent)
|
||||
cy.findAllByText('Reject change').first().click({ force: true })
|
||||
cy.contains('.toolbar-item', 'Review').click()
|
||||
|
||||
cy.log('recompile to force flush')
|
||||
recompile()
|
||||
|
||||
cy.log('verify the changes are applied in the visual editor')
|
||||
cy.findByText('Visual Editor').click()
|
||||
cy.get('.cm-content').should('contain.text', oldContent)
|
||||
|
||||
cy.log('disable track-changes for everyone again')
|
||||
toggleTrackChanges(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('editor', () => {
|
||||
it('renders jpg', () => {
|
||||
cy.findByTestId('file-tree').findByText('frog.jpg').click()
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('git-bridge', function () {
|
||||
|
||||
function maybeClearAllTokens() {
|
||||
cy.visit('/user/settings')
|
||||
cy.findByText('Git integration')
|
||||
cy.findByText('Git Integration')
|
||||
cy.get('button')
|
||||
.contains(/Generate token|Add another token/)
|
||||
.then(btn => {
|
||||
@@ -63,7 +63,7 @@ describe('git-bridge', function () {
|
||||
it('should render the git-bridge UI in the settings', () => {
|
||||
maybeClearAllTokens()
|
||||
cy.visit('/user/settings')
|
||||
cy.findByText('Git integration')
|
||||
cy.findByText('Git Integration')
|
||||
cy.get('button').contains('Generate token').click()
|
||||
cy.get('code')
|
||||
.contains(/olp_[a-zA-Z0-9]{16}/)
|
||||
@@ -93,7 +93,7 @@ describe('git-bridge', function () {
|
||||
cy.get('code').contains(`git clone ${gitURL(id.toString())}`)
|
||||
})
|
||||
cy.findByRole('button', {
|
||||
name: /generate token/i,
|
||||
name: 'Generate token',
|
||||
}).click()
|
||||
cy.get('code').contains(/olp_[a-zA-Z0-9]{16}/)
|
||||
})
|
||||
@@ -107,7 +107,7 @@ describe('git-bridge', function () {
|
||||
cy.get('code').contains(`git clone ${gitURL(id.toString())}`)
|
||||
})
|
||||
cy.findByText('Generate token').should('not.exist')
|
||||
cy.findByText(/generate a new one in Account settings/i)
|
||||
cy.findByText(/generate a new one in Account Settings/)
|
||||
cy.findByText('Go to settings')
|
||||
.should('have.attr', 'target', '_blank')
|
||||
.and('have.attr', 'href', '/user/settings')
|
||||
@@ -196,7 +196,7 @@ describe('git-bridge', function () {
|
||||
cy.get('code').contains(`git clone ${gitURL(projectId.toString())}`)
|
||||
})
|
||||
cy.findByRole('button', {
|
||||
name: /generate token/i,
|
||||
name: 'Generate token',
|
||||
}).click()
|
||||
cy.get('code')
|
||||
.contains(/olp_[a-zA-Z0-9]{16}/)
|
||||
@@ -365,7 +365,7 @@ Hello world
|
||||
it('should not render the git-bridge UI in the settings', () => {
|
||||
login('user@example.com')
|
||||
cy.visit('/user/settings')
|
||||
cy.findByText('Git integration').should('not.exist')
|
||||
cy.findByText('Git Integration').should('not.exist')
|
||||
})
|
||||
it('should not render the git-bridge UI in the editor', function () {
|
||||
login('user@example.com')
|
||||
|
||||
@@ -5,11 +5,11 @@ import { v4 as uuid } from 'uuid'
|
||||
export function createProject(
|
||||
name: string,
|
||||
{
|
||||
type = 'Blank project',
|
||||
type = 'Blank Project',
|
||||
newProjectButtonMatcher = /new project/i,
|
||||
open = true,
|
||||
}: {
|
||||
type?: 'Blank project' | 'Example project'
|
||||
type?: 'Blank Project' | 'Example Project'
|
||||
newProjectButtonMatcher?: RegExp
|
||||
open?: boolean
|
||||
} = {}
|
||||
@@ -37,7 +37,7 @@ export function createProject(
|
||||
}
|
||||
cy.findAllByRole('button').contains(newProjectButtonMatcher).click()
|
||||
// FIXME: This should only look in the left menu
|
||||
cy.findAllByText(new RegExp(type, 'i')).first().click()
|
||||
cy.findAllByText(type).first().click()
|
||||
cy.findByRole('dialog').within(() => {
|
||||
cy.get('input').type(name)
|
||||
cy.findByText('Create').click()
|
||||
@@ -215,3 +215,37 @@ export function createNewFile() {
|
||||
|
||||
return fileName
|
||||
}
|
||||
|
||||
export function toggleTrackChanges(state: boolean) {
|
||||
cy.findByText('Review').click()
|
||||
cy.get('.track-changes-menu-button').then(el => {
|
||||
// when the menu is expanded renders the `expand_more` icon,
|
||||
// and the `chevron_right` icon when it's collapsed
|
||||
if (!el.text().includes('expand_more')) {
|
||||
el.click()
|
||||
}
|
||||
})
|
||||
|
||||
cy.findByText('Everyone')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('.form-check-input').then(el => {
|
||||
if (el.prop('checked') === state) return
|
||||
|
||||
const id = uuid()
|
||||
const alias = `@${id}`
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: '**/track_changes',
|
||||
times: 1,
|
||||
}).as(id)
|
||||
if (state) {
|
||||
cy.get('.form-check-input').check()
|
||||
} else {
|
||||
cy.get('.form-check-input').uncheck()
|
||||
}
|
||||
cy.wait(alias)
|
||||
})
|
||||
})
|
||||
cy.contains('.toolbar-item', 'Review').click()
|
||||
}
|
||||
|
||||
@@ -131,7 +131,9 @@ const allowedVars = Joi.object(
|
||||
'GIT_BRIDGE_HOST',
|
||||
'GIT_BRIDGE_PORT',
|
||||
'V1_HISTORY_URL',
|
||||
'DOCKER_RUNNER',
|
||||
'SANDBOXED_COMPILES',
|
||||
'SANDBOXED_COMPILES_SIBLING_CONTAINERS',
|
||||
'ALL_TEX_LIVE_DOCKER_IMAGE_NAMES',
|
||||
'OVERLEAF_TEMPLATES_USER_ID',
|
||||
'OVERLEAF_NEW_PROJECT_TEMPLATE_LINKS',
|
||||
@@ -194,7 +196,10 @@ function setVarsDockerCompose({ pro, vars, version, withDataDir }) {
|
||||
)
|
||||
}
|
||||
|
||||
if (cfg.services.sharelatex.environment.SANDBOXED_COMPILES === 'true') {
|
||||
if (
|
||||
cfg.services.sharelatex.environment
|
||||
.SANDBOXED_COMPILES_SIBLING_CONTAINERS === 'true'
|
||||
) {
|
||||
cfg.services.sharelatex.environment.SANDBOXED_COMPILES_HOST_DIR =
|
||||
PATHS.SANDBOXED_COMPILES_HOST_DIR
|
||||
cfg.services.sharelatex.environment.TEX_LIVE_DOCKER_IMAGE =
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('Project List', () => {
|
||||
|
||||
before(() => {
|
||||
login(REGULAR_USER)
|
||||
createProject(projectName, { type: 'Example project', open: false })
|
||||
createProject(projectName, { type: 'Example Project', open: false })
|
||||
})
|
||||
beforeEach(function () {
|
||||
login(REGULAR_USER)
|
||||
|
||||
@@ -10,7 +10,9 @@ const LABEL_TEX_LIVE_VERSION = 'TeX Live version'
|
||||
|
||||
describe('SandboxedCompiles', function () {
|
||||
const enabledVars = {
|
||||
DOCKER_RUNNER: 'true',
|
||||
SANDBOXED_COMPILES: 'true',
|
||||
SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true',
|
||||
ALL_TEX_LIVE_DOCKER_IMAGE_NAMES: '2023,2022',
|
||||
}
|
||||
|
||||
@@ -59,9 +61,7 @@ describe('SandboxedCompiles', function () {
|
||||
})
|
||||
|
||||
function checkSyncTeX() {
|
||||
// TODO(25342): re-enable
|
||||
// eslint-disable-next-line mocha/no-skipped-tests
|
||||
describe.skip('SyncTeX', function () {
|
||||
describe('SyncTeX', function () {
|
||||
let projectName: string
|
||||
beforeEach(function () {
|
||||
projectName = `Project ${uuid()}`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user