Compare commits

..
129 Commits
Author SHA1 Message Date
yu-i-i 38250cd239 Reference picker: fix show existing key as unexistent in Insert mode 2026-07-23 17:38:45 +02:00
yu-i-i c7579e3e74 Update README.md for 6.2.0 2026-07-17 17:08:43 +02:00
yu-i-i fa93cbab49 Add syscalls to seccomp profile, fixes #194 2026-07-17 01:54:20 +02:00
yu-i-i f3075e1a0c Admin tools: improve Active Projects UI
- do not show the project owner, email and connections number columns
 - add mailto: all active users of a project when clicking the project title
 - fix error handling
2026-07-17 01:54:20 +02:00
yu-i-i 661bbf1de1 Fix: hide Git Bridge, GitHub Sync and Zotero UI elements when the corresponding feature is disabled 2026-07-17 01:54:20 +02:00
yu-i-i 341ddc2106 Reference picker: improve usability
- show tags for nonexistent references
- click a badge to find the corresponding reference
2026-07-17 01:54:20 +02:00
yu-i-i 77cae80c16 Add variables to enable experimental features: pandoc conversion and editor tabs 2026-07-17 01:54:19 +02:00
yu-i-i c8baf113d4 Registration page: the initial commit 2026-07-17 01:54:13 +02:00
yu-i-i 82d8e2fde3 Git bridge, Template gallery: remove integration with the old editor code 2026-07-17 01:54:13 +02:00
yu-i-i d0735d5d8a Admin tools: handle bad SMTP configuration error on account creation 2026-07-17 01:54:13 +02:00
yu-i-i 3828f4c51c Admin tools: remove themed pages env var; style admin header
- themed pages now is enabled by default upstream
  - make the header background red on admin manage urers/projects pages
  - fix minor bug
2026-07-17 01:53:53 +02:00
yu-i-i 1e35481223 Github Sync: enhance token handling and post-sync indexing
- support file-based token cipher
  - reindex references after sync
  - fix typo and clean up code
2026-07-16 17:34:49 +02:00
yu-i-i 5d3441e08f Zotero: improve integration and reference import
- use OAuth instead of tokens
  - add an integration card
  - improve error handling
  - add BibLaTeX format support
  - fix indexing of linked .bib files on creation
2026-07-16 17:34:16 +02:00
yu-i-i a55f2a497f Linked URL: adapt for yarn; bump node to 24.14.1 2026-07-08 16:27:40 +02:00
David Rotermund 5e465336ba Fix admin socket metadata handling and footer HTML rendering in admin-tools 2026-07-07 14:42:59 +02:00
Pierpaolo Vittorini f13a251381 Admin tools, Authentication: Fix: set analyticsId when creating a new user 2026-07-07 14:42:59 +02:00
yu-i-i 1e0e9473d2 Zotero: allow collaborators to refresh files imported from Zotero; show importer's name in the header 2026-07-07 14:42:59 +02:00
yu-i-i 9a1d84887a Admin tools: move styles to modules directory; fix form for dark theme 2026-07-07 14:42:59 +02:00
yu-i-i 6c4ba6ff5e Github Sync: initial commit 2026-07-07 14:42:59 +02:00
yu-i-i dc99fbc810 Template Gallery: fix for 6.2.0 2026-07-07 14:42:59 +02:00
yu-i-i ccecdc3fb5 Git integration: use named export for settings page widget
remove stray console.log
2026-07-07 14:42:59 +02:00
Synésio Neto d4e0184b5e Fix a greyed out create file button after the Zotero Integration was merged. 2026-07-07 14:42:59 +02:00
yu-i-i 92fd2b0997 Sandboxed compiles: cwd argument was added to CommandRunner
see upstream commit ce6f9b8
2026-07-07 14:42:59 +02:00
yu-i-i ff9872d8bf Patches handled by yarn 2026-07-07 14:42:58 +02:00
yu-i-i 8bf99c7e29 Revert "[clsi] remove locking from docker actions (#32373)"
This reverts commit d66a856baa.
2026-07-07 14:42:58 +02:00
yu-i-i df7a4fa9ff Reference picker: normal search instead of fuzzy; overleafying modal 2026-07-07 14:42:58 +02:00
David Rotermund ea73e68f56 Reference picker 2026-07-07 14:42:58 +02:00
yu-i-i 2d4a923bfc Authentication: fix ldap searchAttributes for passport-ldapauth 3.0.1 2026-07-07 14:42:58 +02:00
David Rotermund 7ae7736a8c Zotero Integration (#164) 2026-07-07 14:42:58 +02:00
David Rotermund b1cf1f3c4b Logo Tools (#158)
* Logo Tools

* Moved to /tools/logo; Checks dependencies first and stops if they are not there; Added information about 3_remove_branding_from_projectpage.sh in README ; reordered the commands of 1_convert.sh to avoid the logo-horizontal.png problem
2026-07-07 14:42:58 +02:00
yu-i-i 7e7db051e5 Git Bridge: fix incorrect token creation date display 2026-07-07 14:42:58 +02:00
David Rotermund 820f3a1e2d Admin tools: introduce Activ Projects tab on the Manage Site page
- code moved to modules/admin-tools (yu-i-i)
2026-07-07 14:42:58 +02:00
yu-i-i b569fad605 Git Bridge: route PAT validation endpoint to correct router 2026-07-07 14:42:58 +02:00
yu-i-i 0829a07998 Sandboxed compiles: avoid destroying containers of recently accessed projects
see upstream commit a9c4138
  here is an appropriate DockerRunner.mjs patch
2026-07-07 14:42:58 +02:00
yu-i-i ec4ad68791 Admin tools: themed dashboard 2026-07-07 14:42:57 +02:00
Tao Chen 623f78905b Git Bridge: support git-bridge configuration via env in nginx
- generate overleaf.conf dynamically
- support GIT_BRIDGE_HOST and GIT_BRIDGE_PORT
- configure client_max_body_size for /git using GIT_BRIDGE_REPOSTORE_MAX_FILE_SIZE
2026-07-07 14:42:57 +02:00
yu-i-i 031d83a605 Linked URL: bump node to 24.13.0 2026-07-07 14:42:57 +02:00
yu-i-i bbb212bdf6 Git Bridge: integrations-panel code was moved 2026-07-07 14:42:57 +02:00
yu-i-i 0567c8927e Authentication: fix uid ldap login after email parsing was added to rate limiter 2026-07-07 14:42:57 +02:00
yu-i-i 4ef5f28282 Sandboxed compiles: convert DockerRunner to ESM 2026-07-07 14:42:57 +02:00
yu-i-i 32ee988a26 Update README.md 2026-07-07 14:42:57 +02:00
yu-i-i 9374c22bb6 Sandboxed compiles: add scripts to add/strip repo prefix from texlive images 2026-07-07 14:42:57 +02:00
yu-i-i a47638c120 Add env variable to enable restoring project/file from history snapshot 2026-07-07 14:42:57 +02:00
yu-i-i a7349c0c7e Admin tools: minor improvements
- enable Projects button in navbar for admin projects page
- enable copy of user ID in user info modal
- add null check in UserListController (thanks @moschlar)
2026-07-07 14:42:57 +02:00
yu-i-i 83f2ec5f1c Git Bridge: Add git integration 2026-07-07 14:42:57 +02:00
yu-i-i 5c68279105 Sandboxed compiles: set imageName to default if undefined in project 2026-07-07 14:42:56 +02:00
yu-i-i e226d1b179 Authentication: update passport-ldapauth to 3.0.1; fix minor issues 2026-07-07 14:42:56 +02:00
yu-i-i e59d381d60 Linked URL: prevent SSRF via DNS rebinding; minor fixes 2026-07-07 14:42:56 +02:00
yu-i-i b6e33c120d Template gallery: add template management authorization middleware
- Middleware to enforce template access rules
- Introduce template manager role
- Fix minor issues
2026-07-07 14:42:56 +02:00
yu-i-i 9472d977b0 Admin tools: optimize user/project list performance
Add frontend pagination
Add search debouncing
Render modals conditionally
Remove unnecessary sorting
2026-07-07 14:42:56 +02:00
yu-i-i 5673353770 Update README.md 2026-07-07 14:42:56 +02:00
yu-i-i 942bd4b7f8 Sandboxed Compiles: pass imageName in Copy project 2026-07-07 14:42:56 +02:00
yu-i-i 5db0f4fece Admin tools: fix minor bugs 2026-07-07 14:42:56 +02:00
yu-i-i 9db3d2cda3 Sandboxed compiles: add arm64 support to clsi seccomp profile 2026-07-07 14:42:56 +02:00
yu-i-i d871ce961a Sandboxed Compiles: support configurable texlive image root via env var 2026-07-07 14:42:56 +02:00
yu-i-i 0f8e6979e2 Admin Tools: Fix EXTERNAL_AUTH is 'none' 2026-07-07 14:42:55 +02:00
yu-i-i c3814a9062 Admin Tools: Fix sorting 2026-07-07 14:42:55 +02:00
yu-i-i de346104a1 Template Gallery: migration to 6.1.0
enable menu for editor redisign
move frontend code to modules
2026-07-07 14:42:55 +02:00
yu-i-i 4499088928 Symbol Palette: fix for 6.1.0 2026-07-07 14:42:55 +02:00
yu-i-i 8a3ff69f3e Track changes: Fix import after upstream converted several files to ESM 2026-07-07 14:42:55 +02:00
yu-i-i 76623f85eb Authentication: Fix import after upstream converted some files to ESM 2026-07-07 14:42:55 +02:00
yu-i-i 1348946421 Admin Tools: Fix for 6.1.0 migraton 2026-07-07 14:42:55 +02:00
Guilhem Poy 1f9d03c389 Add support for EMAIL_SMTP_PASS_FILE
Small feature to provide the email password from a file rather than directly from the env variable OVERLEAF_EMAIL_SMTP_PASS. Useful when used in conjunction with Docker Compose secrets.
2026-07-07 14:42:55 +02:00
yu-i-i 51a9ce881c Admin Tools: Fix user name
fix user name is not updated

fix first_name/last_name is undefined
2026-07-07 14:42:55 +02:00
yu-i-i 59a140fe30 Admin Tools: Manage users and Manage projects pages 2026-07-07 14:42:55 +02:00
yu-i-i 13fb6cd418 Hotfix 5.5.6-ext-3.3 2026-07-07 14:42:54 +02:00
yu-i-i 343a7de538 Linked URL: add sanitize and normalize 2026-07-07 14:42:54 +02:00
yu-i-i 0b2b709638 README for extended CE 2026-07-07 14:42:54 +02:00
yu-i-i b4896abdea Add service for Linked URL feature 2026-07-07 14:42:54 +02:00
yu-i-i b72a9f9e42 Sandboxed compiles: adjust clsi-profile 2026-07-07 14:42:54 +02:00
yu-i-i 164f160243 Track changes: Fix import after upstream converted DocumentUpdaterHandler and CollaboratorsGetter to ESM 2026-07-07 14:42:54 +02:00
yu-i-i 6a4647ee60 Authentication: Fix import after upstream converted AuthenticationController to ESM 2026-07-07 14:42:54 +02:00
yu-i-i 4a9e22c487 Template gallery: Fix import after upstream converted AuthenticationController, ProjectGetter, ProjectLocator and DocumentUpdaterHandler` to ESM 2026-07-07 14:42:54 +02:00
yu-i-i b062f92ce9 Template gallery: Fix import after upstream converted CompileManager, ClsiManager and RateLimiterMiddleware to ESM 2026-07-07 14:42:54 +02:00
yu-i-i 6c97b44b5b Template gallery: Fix OLModal usage (see upstream commit 19b3834) 2026-07-07 14:42:53 +02:00
yu-i-i cb2a4739ff Authentication: Fix import after upstream converted PermissionsManager, RateLimiterMiddleware, UserCreator and SAMLIdentityManager to ESM 2026-07-07 14:42:53 +02:00
yu-i-i 9755e7f500 Track changes: Fix import after upstream converted ChatManager, UserInfoManager and UserInfoController to ESM 2026-07-07 14:42:53 +02:00
Rudy ERCEK 6dec15bb6f Add MAX_UPLOAD_SIZE environment variable 2026-07-07 14:42:53 +02:00
yu-i-i 4e77cc0471 Fix style of Templates button in navbar 2026-07-07 14:42:53 +02:00
yu-i-i 60c66ad1d5 Fix: comments thread does not necessarily belong to current document 2026-07-07 14:42:53 +02:00
yu-i-i c9f570f108 Fix import after upstream converted AuthorizationMiddleware to ESM 2026-07-07 14:42:53 +02:00
yu-i-i 4d5be374cd Track changes: Use getProjectRanges introduced in the upstream 2026-07-07 14:42:53 +02:00
yu-i-i 9880977b34 Rename @node-saml/node-saml patch for v5.1.0 2026-07-07 14:42:53 +02:00
yu-i-i f76afc3aba Fix imports after upstream converted UserController and CaptchaMiddleware to ESM 2026-07-07 14:42:53 +02:00
yu-i-i 1e53bff130 Fix import after upstream converted ErrorController to ESM 2026-07-07 14:42:53 +02:00
yu-i-i 6becc486fa Bring back deleteFile removed upstream 2026-07-07 14:42:53 +02:00
yu-i-i 8f5267cde9 Symbol palette: ui components were moved to shared folder 2026-07-07 14:42:52 +02:00
yu-i-i 4e42c6d232 Move Sympol Palette code to modules, closes #63 2026-07-07 14:42:52 +02:00
yu-i-i f502d4e4b5 Template gallery: ui components were moved to shared folder 2026-07-07 14:42:52 +02:00
yu-i-i e965b4f0f0 passport-saml was updated to v5.1.0 2026-07-07 14:42:52 +02:00
yu-i-i 5f8118ee61 Fix: always include repository prefix in sandboxed compiles image name 2026-07-07 14:42:52 +02:00
yu-i-i effbd8ed6b Fix template publishing to align with upstream project context changes 2026-07-07 14:42:52 +02:00
yu-i-i 02c474afbb Remove LDAP admin check via group search in ldap-passport 2026-07-07 14:42:52 +02:00
yu-i-i 73b49a0dfe toggleSymbolPalette now in useEditorPropertiesContext 2026-07-07 14:42:52 +02:00
yu-i-i 65af49b820 Set use-ai capability to false 2026-07-07 14:42:52 +02:00
yu-i-i 3a1e4f13ed Track changes: convert to mjs 2026-07-07 14:42:52 +02:00
yu-i-i 4671f26904 Track changes: use getTrackedChangesUserIds introduced in the mainstream 2026-07-07 14:42:52 +02:00
yu-i-i 23fb6e3ebb Remove OVERLEAF_OIDC_DISABLE_JIT_ACCOUNT_CREATION in favor of OVERLEAF_OIDC_ALLOWED_EMAIL_DOMAINS 2026-07-07 14:42:52 +02:00
Synésio Neto a2416a499c Introduce an environment variable to allow JIT OIDC users creation based on their email address domain. 2026-07-07 14:42:52 +02:00
yu-i-i ff04f9a073 Introduce an environment variable to control user creation in OIDC authentication, closes #47 2026-07-07 14:42:51 +02:00
David Rotermund e534b32953 I need the --network=host settings for building the container 2026-07-07 14:42:51 +02:00
yu-i-i da1567cc51 Avoid DEP0174 by removing async from callback-based getGroupPolicyForUser 2026-07-07 14:42:51 +02:00
yu-i-i f59d616422 Fix login page 2026-07-07 14:42:51 +02:00
yu-i-i e094fb59df Symbol Palette: make close button visible 2026-07-07 14:42:51 +02:00
yu-i-i efc0289497 Template Gallery: replace markdown-it with marked 2026-07-07 14:42:51 +02:00
yu-i-i bcb5af48ae Refactor Template Gallery; resolves #38 and #39
- Replace free-text license input with a select box
- Improve visual presentation of modals and enhance keyboard interaction
2026-07-07 14:42:51 +02:00
yu-i-i e6db566a9c Refactor Sandboxed Compiles 2026-07-07 14:42:51 +02:00
yu-i-i c7350d5877 Symbol Palette: improve keyboard input experience 2026-07-07 14:42:51 +02:00
yu-i-i 3f6acabf26 Symbol Palette: get rid of @reach/tabs 2026-07-07 14:42:51 +02:00
yu-i-i dc78d3f028 Add 'poll' to clsi seccomp profile, fixes minted. Thanks, David. 2026-07-07 14:42:51 +02:00
yu-i-i 6b3472e3a8 Make Template Gallery optional; rename environment variables 2026-07-07 14:42:51 +02:00
yu-i-i 8dc220fdc5 Add Template Gallery support 2026-07-07 14:42:50 +02:00
yu-i-i ffe7d44174 Track changes / comments: update backend to support frontend changes 2026-07-07 14:42:50 +02:00
yu-i-i 5b9c6985d4 Allow EXTERNAL_AUTH to be undefined, fixes #26 2026-07-07 14:42:50 +02:00
yu-i-i 6e4e540c5a Symbol palette: switch to 'OL' UI components and apply minor cosmetic changes 2026-07-07 14:42:50 +02:00
yu-i-i 837519b08d Make OVERLEAF_OIDC_USER_ID_FIELD support 'email' as a value 2026-07-07 14:42:50 +02:00
yu-i-i 9bc7fcf77a See upstream commit 42ee56e 2026-07-07 14:42:50 +02:00
yu-i-i dbd14d5e86 Fix glitches in symbol palette after switching to Bootstrap 5 2026-07-07 14:42:50 +02:00
yu-i-i 36719cbf3a Whitelist /oidc/login endpoint, fixes #21 2026-07-07 14:42:50 +02:00
yu-i-i 899d4cd66f Add ENV variables to control SAML signature validation 2026-07-07 14:42:50 +02:00
yu-i-i 3bd8b49ae1 Re-export doLogout (was removed from exports in commit b9fb636). 2026-07-07 14:42:50 +02:00
yu-i-i cfeed73e52 Refactor authentication code; add OIDC support 2026-07-07 14:42:49 +02:00
yu-i-i 81d51d1498 Allow adding extra flags to LaTeX compiler through environment variable 2026-07-07 14:42:49 +02:00
yu-i-i bbd5300f66 Enable LDAP and SAML authentication support 2026-07-07 14:42:49 +02:00
yu-i-i 8dea2fab50 Enable Symbol Palette 2026-07-07 14:42:49 +02:00
yu-i-i a66d4389bf Allow selecting a TeX Live image for a project 2026-07-07 14:42:49 +02:00
Sam Van den Vonder 9aee49e3b8 Enable Sandboxed Compiles feature 2026-07-07 14:42:49 +02:00
yu-i-i 636231490e Enable track changes and comments feature 2026-07-07 02:49:07 +02:00
yu-i-i 0d0450b7ca Redirect non-existing links to Overleaf page 2026-07-07 02:49:06 +02:00
393 changed files with 2571 additions and 24455 deletions
-5
View File
@@ -4,8 +4,3 @@
# user defined files # user defined files
.env .env
docker-compose.override.yml docker-compose.override.yml
# local-only agent docs + plan notes (never track: they hold credentials/creds refs)
references/
AGENTS.md
todo.md
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/access-token-encryptor monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/access-token-encryptor
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/access-token-encryptor
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/access-token-encryptor
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
@@ -5,5 +5,6 @@ access-token-encryptor
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/access-token-encryptor
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/fetch-utils monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/fetch-utils
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/fetch-utils
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/fetch-utils
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ fetch-utils
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/fetch-utils
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/logger monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/logger
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/logger
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/logger
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ logger
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/logger
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/metrics monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/metrics
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/metrics
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/metrics
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ metrics
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/metrics
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/mongo-utils monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/mongo-utils
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/mongo-utils
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/mongo-utils
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,6 +5,7 @@ mongo-utils
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/mongo-utils
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
--tsconfig-no-implicit-any=True --tsconfig-no-implicit-any=True
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/o-error monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/o-error
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/o-error
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/o-error
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ o-error
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/o-error
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/object-persistor monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/object-persistor
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/object-persistor
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/object-persistor
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
@@ -5,6 +5,7 @@ object-persistor
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/object-persistor
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
--tsconfig-no-implicit-any=True --tsconfig-no-implicit-any=True
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/overleaf-editor-core monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/overleaf-editor-core
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/overleaf-editor-core
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/overleaf-editor-core
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
@@ -5,5 +5,6 @@ overleaf-editor-core
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=overleaf-editor-core
--pipeline-owner=44 --pipeline-owner=44
--public-repo=False --public-repo=False
@@ -56,7 +56,8 @@ class TrackedChange {
return false return false
} }
return ( return (
this.tracking.canMergeWith(other.tracking) && this.tracking.type === other.tracking.type &&
this.tracking.userId === other.tracking.userId &&
this.range.touches(other.range) && this.range.touches(other.range) &&
this.range.canMerge(other.range) this.range.canMerge(other.range)
) )
@@ -74,7 +75,13 @@ class TrackedChange {
} }
return new TrackedChange( return new TrackedChange(
this.range.merge(other.range), this.range.merge(other.range),
this.tracking.mergeWith(other.tracking) new TrackingProps(
this.tracking.type,
this.tracking.userId,
this.tracking.ts.getTime() > other.tracking.ts.getTime()
? this.tracking.ts
: other.tracking.ts
)
) )
} }
@@ -405,49 +405,6 @@ describe('TextOperation', function () {
}) })
) )
it(
'compose associativity (randomised)',
random.test(numTrials, () => {
const str = random.string(20)
const comments = random.comments(6)
const a = randomOperation(str, comments.ids)
const afterA = new StringFileData(str, comments.comments)
a.apply(afterA)
const b = randomOperation(afterA.getContent(), comments.ids)
const afterB = new StringFileData(
afterA.getContent(),
comments.comments
)
b.apply(afterB)
const c = randomOperation(afterB.getContent(), comments.ids)
const ab = a.compose(b)
const ab_c = ab.compose(c)
const bc = b.compose(c)
const a_bc = a.compose(bc)
const ab_c_file = new StringFileData(str, comments.comments)
ab_c.apply(ab_c_file)
const a_bc_file = new StringFileData(str, comments.comments)
a_bc.apply(a_bc_file)
const fuzzingError = fuzzingErrorMessage({
str,
comments,
a: a.toJSON(),
b: b.toJSON(),
c: c.toJSON(),
})
expect(ab_c_file.toRaw()).to.deep.equal(a_bc_file.toRaw(), fuzzingError)
})
)
it('composes two operations with comments', function () { it('composes two operations with comments', function () {
expect( expect(
compose( compose(
@@ -668,54 +625,11 @@ describe('TextOperation', function () {
a: a.toJSON(), a: a.toJSON(),
b: b.toJSON(), b: b.toJSON(),
}) })
// The composition of ab' and ba' is not guaranteed to be equal, but expect(abPrime.equals(baPrime)).to.be.equal(true, fuzzingError)
// should converge to the same file contents + ranges.
expect(abFile.toRaw()).to.deep.equal(baFile.toRaw(), fuzzingError) expect(abFile.toRaw()).to.deep.equal(baFile.toRaw(), fuzzingError)
}) })
) )
it('chooses lower tracked change timestamp', function () {
const ts1 = '2024-01-01T01:00:00.000Z'
const ts2 = '2024-01-01T02:00:00.000Z'
const str = 'abcde'
const comments = []
const a = new TextOperation()
.retain(2, {
tracking: TrackingProps.fromRaw({
ts: ts1,
type: 'insert',
userId: 'user1',
}),
})
.retain(1)
.retain(2, {
tracking: TrackingProps.fromRaw({
ts: ts2,
type: 'insert',
userId: 'user1',
}),
})
const b = new TextOperation().retain(1).remove(3).retain(1)
const [aPrime, bPrime] = TextOperation.transform(a, b)
const aComposeBPrime = a.compose(bPrime)
const bComposeAPrime = b.compose(aPrime)
const aBPFile = new StringFileData(str, comments)
aComposeBPrime.apply(aBPFile)
const bAPFile = new StringFileData(str, comments)
bComposeAPrime.apply(bAPFile)
expect(aBPFile.toRaw()).to.deep.equal(bAPFile.toRaw())
expect(aBPFile.trackedChanges.length).to.equal(1)
expect(
aBPFile.trackedChanges.asSorted()[0].tracking.ts.toISOString()
).to.equal(ts1)
})
it('adds a tracked change from operation 1', function () { it('adds a tracked change from operation 1', function () {
expect( expect(
transform( transform(
@@ -8,7 +8,7 @@ const { expect } = require('chai')
describe('TrackedChangeList', function () { describe('TrackedChangeList', function () {
describe('applyInsert', function () { describe('applyInsert', function () {
describe('with same author', function () { describe('with same author', function () {
it('should merge consecutive tracked changes and use the earliest timestamp', function () { it('should merge consecutive tracked changes and use the latest timestamp', function () {
const trackedChanges = TrackedChangeList.fromRaw([ const trackedChanges = TrackedChangeList.fromRaw([
{ {
range: { pos: 0, length: 3 }, range: { pos: 0, length: 3 },
@@ -33,7 +33,7 @@ describe('TrackedChangeList', function () {
tracking: { tracking: {
type: 'insert', type: 'insert',
userId: 'user1', userId: 'user1',
ts: '2023-01-01T00:00:00.000Z', ts: '2024-01-01T00:00:00.000Z',
}, },
}, },
]) ])
@@ -699,7 +699,7 @@ describe('TrackedChangeList', function () {
tracking: { tracking: {
type: 'insert', type: 'insert',
userId: 'user1', userId: 'user1',
ts: '2023-01-01T00:00:00.000Z', ts: '2024-01-01T00:00:00.000Z',
}, },
}, },
]) ])
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/promise-utils monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/promise-utils
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/promise-utils
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/promise-utils
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ promise-utils
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/promise-utils
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/ranges-tracker monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/ranges-tracker
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/ranges-tracker
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/ranges-tracker
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ ranges-tracker
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/ranges-tracker
--pipeline-owner=44 --pipeline-owner=44
--public-repo=False --public-repo=False
+1 -16
View File
@@ -420,7 +420,7 @@ class RangesTracker {
offset = opStart - changeStart offset = opStart - changeStart
change.op.i = change.op.i =
change.op.i.slice(0, offset) + op.i + change.op.i.slice(offset) change.op.i.slice(0, offset) + op.i + change.op.i.slice(offset)
change.metadata.ts = pickTimestamp(change.metadata, metadata) change.metadata.ts = metadata.ts
alreadyMerged = true alreadyMerged = true
movedChanges.push(change) movedChanges.push(change)
} else if (opStart <= changeStart) { } else if (opStart <= changeStart) {
@@ -728,10 +728,6 @@ class RangesTracker {
) { ) {
removeChanges.push(change) removeChanges.push(change)
previousChange.op.i += change.op.i previousChange.op.i += change.op.i
previousChange.metadata.ts = pickTimestamp(
previousChange.metadata,
change.metadata
)
movedChanges.push(previousChange) movedChanges.push(previousChange)
} }
} else if ( } else if (
@@ -794,15 +790,4 @@ class RangesTracker {
} }
} }
function pickTimestamp(oldMetadata, newMetadata) {
// Make sure null values don't get treated as 1970-01-01 dates in the
// comparison below.
if (oldMetadata.ts == null) return newMetadata.ts
if (newMetadata.ts == null) return oldMetadata.ts
return new Date(oldMetadata.ts) < new Date(newMetadata.ts)
? oldMetadata.ts
: newMetadata.ts
}
module.exports = RangesTracker module.exports = RangesTracker
@@ -222,114 +222,4 @@ describe('RangesTracker', function () {
) )
}) })
}) })
describe('merging tracked inserts from the same user', function () {
beforeEach(function () {
this.comments = []
})
it('keeps the earlier timestamp when the existing insert is older', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'foo' },
metadata: { user_id: 'user-1', ts: '2024-01-01T00:00:00.000Z' },
},
],
this.comments
)
this.rangesTracker.track_changes = true
this.rangesTracker.applyOp(
{ p: 13, i: 'bar' },
{ user_id: 'user-1', ts: new Date('2024-06-01T00:00:00.000Z') }
)
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(change.op).to.deep.equal({ p: 10, i: 'foobar' })
expect(change.metadata.user_id).to.equal('user-1')
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
it('keeps the earlier timestamp when the incoming insert is older', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'foo' },
metadata: { user_id: 'user-1', ts: '2024-06-01T00:00:00.000Z' },
},
],
this.comments
)
this.rangesTracker.track_changes = true
this.rangesTracker.applyOp(
{ p: 13, i: 'bar' },
{ user_id: 'user-1', ts: new Date('2024-01-01T00:00:00.000Z') }
)
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(change.op).to.deep.equal({ p: 10, i: 'foobar' })
expect(change.metadata.user_id).to.equal('user-1')
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
it('uses the defined timestamp when the existing insert has a null timestamp', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'foo' },
metadata: { user_id: 'user-1', ts: null },
},
],
this.comments
)
this.rangesTracker.track_changes = true
this.rangesTracker.applyOp(
{ p: 13, i: 'bar' },
{ user_id: 'user-1', ts: new Date('2024-01-01T00:00:00.000Z') }
)
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
it('keeps the earlier timestamp when re-merging inserts after a deletion', function () {
this.rangesTracker = new RangesTracker(
[
{
id: 'id1',
op: { p: 10, i: 'aaa' },
metadata: { user_id: 'user-1', ts: '2024-03-01T00:00:00.000Z' },
},
{
id: 'id2',
op: { p: 13, i: 'bb' },
metadata: { user_id: 'user-2', ts: '2024-02-01T00:00:00.000Z' },
},
{
id: 'id3',
op: { p: 15, i: 'ccc' },
metadata: { user_id: 'user-1', ts: '2024-01-01T00:00:00.000Z' },
},
],
this.comments
)
this.rangesTracker.applyOp({ p: 13, d: 'bb' })
expect(this.rangesTracker.changes).to.have.length(1)
const [change] = this.rangesTracker.changes
expect(change.op).to.deep.equal({ p: 10, i: 'aaaccc' })
expect(change.metadata.user_id).to.equal('user-1')
expect(new Date(change.metadata.ts).toISOString()).to.equal(
'2024-01-01T00:00:00.000Z'
)
})
})
}) })
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/redis-wrapper monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/redis-wrapper
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/redis-wrapper
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/redis-wrapper
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ redis-wrapper
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/redis-wrapper
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/settings monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/settings
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/settings
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/settings
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ settings
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/settings
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/stream-utils monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/stream-utils
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/stream-utils
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/stream-utils
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
+1
View File
@@ -5,5 +5,6 @@ stream-utils
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/stream-utils
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
-59
View File
@@ -1,59 +0,0 @@
# Run the test commands in the scope of the monorepo.
# Eslint and prettier (plus some configs) are on the root.
RUN_LINTING = ../../bin/run -w /overleaf/libraries/validation-tools monorepo yarn run --silent
RUN_LINTING_MONOREPO = ../../bin/run monorepo yarn run --silent
WITH_CACHE_FOLDER = \
lint \
lint_ci \
lint_fix \
format \
format_ci \
format_fix \
typecheck \
typecheck_ci \
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/prettier/
../../node_modules/.cache/prettier/:
mkdir -p $@
$(WITH_CACHE_FOLDER): ../../node_modules/.cache/eslint/
../../node_modules/.cache/eslint/:
mkdir -p $@
format:
$(RUN_LINTING_MONOREPO) format libraries/validation-tools
format_ci:
$(RUN_LINTING_MONOREPO) format libraries/validation-tools
format_fix:
$(RUN_LINTING_MONOREPO) format:fix libraries/validation-tools
lint:
$(RUN_LINTING) lint
lint_ci:
-$(RUN_LINTING) lint --format json --output-file reports/eslint.json
sed -i 's_"filePath":"/overleaf_"filePath":"$(MONOREPO)_g' reports/eslint.json
lint_fix:
$(RUN_LINTING) lint:fix
typecheck:
$(RUN_LINTING) types:check
typecheck_ci:
$(RUN_LINTING) types:check
test: test_unit
test_unit:
ifneq (,$(wildcard test/unit))
$(RUN_LINTING) test:unit
endif
test: test_acceptance
test_acceptance:
ifneq (,$(wildcard test/acceptance))
$(RUN_LINTING) test:acceptance
endif
@@ -5,6 +5,7 @@ validation-tools
--esmock-loader=False --esmock-loader=False
--is-library=True --is-library=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/validation-tools
--public-repo=False --public-repo=False
--test-acceptance-vitest=True --test-acceptance-vitest=True
--test-unit-vitest=True --test-unit-vitest=True
+5 -8
View File
@@ -41,13 +41,12 @@
"sandboxed-module": "patch:sandboxed-module@npm%3A2.0.4#~/.yarn/patches/sandboxed-module-npm-2.0.4-f8b45aacc9.patch", "sandboxed-module": "patch:sandboxed-module@npm%3A2.0.4#~/.yarn/patches/sandboxed-module-npm-2.0.4-f8b45aacc9.patch",
"request/tough-cookie": "5.1.2", "request/tough-cookie": "5.1.2",
"request/form-data": "2.5.5", "request/form-data": "2.5.5",
"request/qs": "6.15.2", "request/qs": "6.14.1",
"@cypress/request/qs": "6.15.2", "@cypress/request/qs": "6.14.1",
"qs": "6.15.2",
"@opentelemetry/api": "1.9.0", "@opentelemetry/api": "1.9.0",
"mocha/serialize-javascript": "7.0.5", "mocha/serialize-javascript": "7.0.5",
"protobufjs": "7.6.3", "pprof/protobufjs": "7.5.5",
"@protobufjs/utf8": "1.1.1", "@google-cloud/profiler/protobufjs": "7.5.5",
"mocha-multi-reporters": "patch:mocha-multi-reporters@npm%3A1.5.1#~/.yarn/patches/mocha-multi-reporters-npm-1.5.1-0a1088aed5.patch", "mocha-multi-reporters": "patch:mocha-multi-reporters@npm%3A1.5.1#~/.yarn/patches/mocha-multi-reporters-npm-1.5.1-0a1088aed5.patch",
"pdfjs-dist": "patch:pdfjs-dist@npm%3A5.1.91#~/.yarn/patches/pdfjs-dist-npm-5.1.91.patch", "pdfjs-dist": "patch:pdfjs-dist@npm%3A5.1.91#~/.yarn/patches/pdfjs-dist-npm-5.1.91.patch",
"referer-parser": "patch:referer-parser@npm%3A0.0.3#~/.yarn/patches/referer-parser-npm-0.0.3.patch", "referer-parser": "patch:referer-parser@npm%3A0.0.3#~/.yarn/patches/referer-parser-npm-0.0.3.patch",
@@ -102,12 +101,11 @@
"cluster-key-slot": "1.1.0", "cluster-key-slot": "1.1.0",
"@octokit/request": "9.2.2", "@octokit/request": "9.2.2",
"randomstring": "1.2.2", "randomstring": "1.2.2",
"vite": "7.3.5", "vite": "7.3.1",
"isomorphic-git": "1.33.1", "isomorphic-git": "1.33.1",
"http-status": "1.5.0", "http-status": "1.5.0",
"knex": "2.4.0", "knex": "2.4.0",
"utf-8-validate": "5.0.8", "utf-8-validate": "5.0.8",
"ws@npm:^8": "8.21.0",
"samlp": "7.0.2", "samlp": "7.0.2",
"compression": "1.7.4", "compression": "1.7.4",
"cookie-parser": "1.4.6", "cookie-parser": "1.4.6",
@@ -200,7 +198,6 @@
}, },
"workspaces": [ "workspaces": [
"jobs/mirror-documentation", "jobs/mirror-documentation",
"jobs/wiki-algolia",
"libraries/*", "libraries/*",
"server-ce/test", "server-ce/test",
"services/analytics", "services/analytics",
-10
View File
@@ -8,13 +8,3 @@ server-ce/test
services/web/scripts/plan-prices services/web/scripts/plan-prices
services/web/scripts/translations services/web/scripts/translations
services/web/scripts/ukamf services/web/scripts/ukamf
# dev-environment node_modules symlinks must never enter the image:
# BuildKit cannot replace a directory already present in the base/cache
# layer (overleaf/services/web/node_modules) with a file/symlink
node_modules
services/web/node_modules
libraries/*/node_modules
services/*/node_modules
services/web/modules/*/node_modules
tools/migrations/node_modules
-120
View File
@@ -2,8 +2,6 @@ import {
createNewFile, createNewFile,
createProjectAndOpenInNewEditor, createProjectAndOpenInNewEditor,
openProjectById, openProjectById,
openProjectByName,
shareProjectByEmailAndAcceptInviteViaDash,
testNewFileUpload, testNewFileUpload,
} from './helpers/project' } from './helpers/project'
import { isExcludedBySharding, startWith } from './helpers/config' import { isExcludedBySharding, startWith } from './helpers/config'
@@ -323,124 +321,6 @@ describe('editor', function () {
}) })
}) })
describe('editing mode', function () {
if (isExcludedBySharding('PRO_DEFAULT_1')) return
startWith({ pro: true })
const OWNER = USER
const EDITOR = COLLABORATOR
const REVIEWER = 'reviewer@example.com'
const VIEWER = 'viewer@example.com'
ensureUserExists({ email: OWNER })
ensureUserExists({ email: EDITOR })
ensureUserExists({ email: REVIEWER })
ensureUserExists({ email: VIEWER })
let projectName: string
function openEditingModeMenu() {
cy.findByRole('button', { name: 'View' }).click()
cy.findByRole('menuitem', { name: 'Editing mode' }).click()
}
beforeWithReRunOnTestRetry(() => {
projectName = `project-${uuid()}`
login(OWNER)
createProjectAndOpenInNewEditor(projectName, { type: 'Example project' })
shareProjectByEmailAndAcceptInviteViaDash(
projectName,
EDITOR,
'Editor',
true
)
login(OWNER)
shareProjectByEmailAndAcceptInviteViaDash(
projectName,
REVIEWER,
'Reviewer',
true
)
login(OWNER)
shareProjectByEmailAndAcceptInviteViaDash(
projectName,
VIEWER,
'Viewer',
true
)
})
it('lets an editor switch between editing and reviewing', function () {
login(EDITOR)
openProjectByName(projectName, true)
openEditingModeMenu()
cy.findByRole('menuitem', { name: 'Editing' })
.should('have.attr', 'aria-selected', 'true')
.and('not.have.attr', 'aria-disabled')
cy.findByRole('menuitem', { name: 'Reviewing' })
.should('have.attr', 'aria-selected', 'false')
.and('not.have.attr', 'aria-disabled')
cy.findByRole('menuitem', { name: 'Viewing' }).should('not.exist')
cy.findByRole('menuitem', { name: 'Reviewing' }).click()
cy.findByRole('button', { name: 'Reviewing' }).should('exist')
openEditingModeMenu()
cy.findByRole('menuitem', { name: 'Reviewing' }).should(
'have.attr',
'aria-selected',
'true'
)
cy.findByRole('menuitem', { name: 'Editing' }).should(
'have.attr',
'aria-selected',
'false'
)
cy.findByRole('menuitem', { name: 'Editing' }).click()
cy.findByRole('button', { name: 'Editing' }).should('exist')
})
it('shows a reviewer in reviewing mode with editing disabled', function () {
login(REVIEWER)
openProjectByName(projectName, true)
openEditingModeMenu()
cy.findByRole('menuitem', { name: 'Reviewing' })
.should('have.attr', 'aria-selected', 'true')
.and('not.have.attr', 'aria-disabled')
cy.findByRole('menuitem', { name: 'Editing' }).should(
'have.attr',
'aria-disabled',
'true'
)
cy.findByRole('menuitem', { name: 'Viewing' }).should('not.exist')
})
it('shows a viewer in viewing mode with editing and reviewing disabled', function () {
login(VIEWER)
openProjectByName(projectName, true)
openEditingModeMenu()
cy.findByRole('menuitem', { name: 'Viewing' }).should(
'have.attr',
'aria-selected',
'true'
)
cy.findByRole('menuitem', { name: 'Editing' }).should(
'have.attr',
'aria-disabled',
'true'
)
cy.findByRole('menuitem', { name: 'Reviewing' }).should(
'have.attr',
'aria-disabled',
'true'
)
})
})
function createRandomLetterString() { function createRandomLetterString() {
const chars = 'abcdefghijklmnopqrstuvwxyz' const chars = 'abcdefghijklmnopqrstuvwxyz'
let result = '' let result = ''
+1 -6
View File
@@ -256,12 +256,7 @@ describe('filestore migration', function () {
}) })
} else { } else {
// 5.x // 5.x
startWith({ startWith({ version: '5.5.5', pro: true, withDataDir: true })
version: '5.5.5',
pro: true,
withDataDir: true,
resetData: true,
})
defaultImage = 'frog.jpg' defaultImage = 'frog.jpg'
ensureUserExists({ email }) ensureUserExists({ email })
before(function () { before(function () {
+2 -2
View File
@@ -206,7 +206,7 @@ export function openProjectViaInviteNotification(
function shareProjectByEmail( function shareProjectByEmail(
projectName: string, projectName: string,
email: string, email: string,
level: 'Viewer' | 'Editor' | 'Reviewer', level: 'Viewer' | 'Editor',
newEditor: boolean = false newEditor: boolean = false
) { ) {
openProjectByName(projectName, newEditor) openProjectByName(projectName, newEditor)
@@ -231,7 +231,7 @@ function shareProjectByEmail(
export function shareProjectByEmailAndAcceptInviteViaDash( export function shareProjectByEmailAndAcceptInviteViaDash(
projectName: string, projectName: string,
email: string, email: string,
level: 'Viewer' | 'Editor' | 'Reviewer', level: 'Viewer' | 'Editor',
newEditor: boolean = false newEditor: boolean = false
) { ) {
shareProjectByEmail(projectName, email, level, newEditor) shareProjectByEmail(projectName, email, level, newEditor)
+1 -1
View File
@@ -21,8 +21,8 @@ import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry
describe('Project Sharing', function () { describe('Project Sharing', function () {
if (isExcludedBySharding('PRO_CUSTOM_4')) return if (isExcludedBySharding('PRO_CUSTOM_4')) return
startWith({ withDataDir: true, pro: true, resetData: true })
ensureUserExists({ email: 'user@example.com' }) ensureUserExists({ email: 'user@example.com' })
startWith({ withDataDir: true, pro: true })
let projectName: string let projectName: string
let recompile: () => void let recompile: () => void
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = chat PROJECT_NAME = chat
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -28,10 +28,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/tools/migrations/package.json \ $(MONOREPO)/tools/migrations/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
+1
View File
@@ -5,5 +5,6 @@ chat
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/chat
--pipeline-owner=44 --pipeline-owner=44
--public-repo=False --public-repo=False
+3 -3
View File
@@ -4,7 +4,7 @@
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/chat/reports - ./reports:/overleaf/services/chat/reports
@@ -21,7 +21,7 @@ services:
condition: service_started condition: service_started
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -42,7 +42,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
@@ -5,6 +5,6 @@ before('run migrations', async function () {
this.timeout(60_000) this.timeout(60_000)
await promisify(exec)( await promisify(exec)(
`cd ../../tools/migrations && yarn run migrations migrate -t 'server-ce & !nonblocking'` `cd ../../tools/migrations && npm run migrations -- migrate -t server-ce`
) )
}) })
+1 -2
View File
@@ -1,5 +1,4 @@
compiles compiles
output output
cache cache
uploads uploads
sharded
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = clsi PROJECT_NAME = clsi
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -29,10 +29,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/services/clsi/package.json \ $(MONOREPO)/services/clsi/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
-5
View File
@@ -192,11 +192,6 @@ async function doCompile(request, stats, timings) {
} }
} }
// Pass through checkpoint setting
if (request.enableCheckpoint) {
env.ENABLE_CHECKPOINT = '1'
}
const compileStart = Date.now() const compileStart = Date.now()
const compileName = getCompileName(request.project_id, request.user_id) const compileName = getCompileName(request.project_id, request.user_id)
@@ -173,7 +173,6 @@ const LATEX_EXPORT_CONFIGS = {
'--to', '--to',
'html', 'html',
'--standalone', '--standalone',
'--mathml',
], ],
}, },
} }
-5
View File
@@ -65,11 +65,6 @@ function parse(body, callback) {
type: 'number', type: 'number',
} }
) )
response.enableCheckpoint = _parseAttribute(
'enableCheckpoint',
compile.options.enableCheckpoint,
{ default: false, type: 'boolean' }
)
response.timeout = _parseAttribute('timeout', compile.options.timeout, { response.timeout = _parseAttribute('timeout', compile.options.timeout, {
default: MAX_TIMEOUT, default: MAX_TIMEOUT,
type: 'number', type: 'number',
-10
View File
@@ -23,15 +23,9 @@ import Metrics from '@overleaf/metrics'
import logger from '@overleaf/logger' import logger from '@overleaf/logger'
import settings from '@overleaf/settings' import settings from '@overleaf/settings'
import ClsiMetrics from './Metrics.js' import ClsiMetrics from './Metrics.js'
import { Minimatch } from 'minimatch'
const { shouldSkipMetrics } = ClsiMetrics const { shouldSkipMetrics } = ClsiMetrics
// Additional file patterns to keep between compiles
const preciousFileMatcher = new Minimatch(settings.preciousFilePattern, {
dot: true,
})
let ResourceWriter let ResourceWriter
const parallelFileDownloads = settings.parallelFileDownloads || 1 const parallelFileDownloads = settings.parallelFileDownloads || 1
@@ -271,10 +265,6 @@ export default ResourceWriter = {
// Epstopdf generated files // Epstopdf generated files
shouldDelete = false shouldDelete = false
} }
// Keep additional precious files and directories
if (shouldDelete && preciousFileMatcher.match(path)) {
shouldDelete = false
}
if ( if (
path === 'output.tar.gz' || path === 'output.tar.gz' ||
path === 'output.synctex.gz' || path === 'output.synctex.gz' ||
+1
View File
@@ -5,6 +5,7 @@ clsi
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/clsi
--pipeline-owner=32 --pipeline-owner=32
--public-repo=True --public-repo=True
--test-unit-vitest=True --test-unit-vitest=True
@@ -35,7 +35,6 @@ module.exports = {
process.env.PDFTOCAIRO_IMAGE || 'quay.io/sharelatex/pdftocairo:24.02', process.env.PDFTOCAIRO_IMAGE || 'quay.io/sharelatex/pdftocairo:24.02',
enablePdfConversions: process.env.ENABLE_PDF_CONVERSIONS === 'true', enablePdfConversions: process.env.ENABLE_PDF_CONVERSIONS === 'true',
maxUploadSize: 50 * 1024 * 1024, maxUploadSize: 50 * 1024 * 1024,
preciousFilePattern: process.env.PRECIOUS_FILE_PATTERN || '',
internal: { internal: {
clsi: { clsi: {
@@ -203,16 +202,6 @@ if ((process.env.DOCKER_RUNNER || process.env.SANDBOXED_COMPILES) === 'true') {
} }
} }
if (process.env.NEW_APPARMOR_PROFILE) {
try {
module.exports.clsi.docker.new_apparmor_profile =
process.env.NEW_APPARMOR_PROFILE
} catch (error) {
console.error(error, 'could not apply new apparmor profile setting')
process.exit(1)
}
}
if (process.env.ALLOWED_IMAGES) { if (process.env.ALLOWED_IMAGES) {
try { try {
module.exports.clsi.docker.allowedImages = module.exports.clsi.docker.allowedImages =
+3 -3
View File
@@ -4,7 +4,7 @@
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./reports:/overleaf/services/clsi/reports - ./reports:/overleaf/services/clsi/reports
- ../../tsconfig.backend.json:/overleaf/tsconfig.backend.json - ../../tsconfig.backend.json:/overleaf/tsconfig.backend.json
@@ -17,7 +17,7 @@ services:
VITEST_NO_CACHE: true VITEST_NO_CACHE: true
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -51,7 +51,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
-1
View File
@@ -31,7 +31,6 @@
"dockerode": "^4.0.9", "dockerode": "^4.0.9",
"express": "4.22.1", "express": "4.22.1",
"lodash": "^4.18.1", "lodash": "^4.18.1",
"minimatch": "^10.2.5",
"multer": "2.1.1", "multer": "2.1.1",
"overleaf-editor-core": "workspace:*", "overleaf-editor-core": "workspace:*",
"p-limit": "^3.1.0", "p-limit": "^3.1.0",
@@ -91,7 +91,6 @@ const LATEX_TO_DOCUMENT_CASES = [
'--to', '--to',
'html', 'html',
'--standalone', '--standalone',
'--mathml',
'--resource-path=..', '--resource-path=..',
'--extract-media=.', '--extract-media=.',
], ],
@@ -19,12 +19,6 @@ describe('ResourceWriter', () => {
}), }),
})) }))
vi.doMock('@overleaf/settings', () => ({
default: (ctx.settings = {
preciousFilePattern: '{keepdir/**,.other/keepdir/**}',
}),
}))
vi.doMock('../../../app/js/ResourceStateManager', () => ({ vi.doMock('../../../app/js/ResourceStateManager', () => ({
default: (ctx.ResourceStateManager = {}), default: (ctx.ResourceStateManager = {}),
})) }))
@@ -252,12 +246,6 @@ describe('ResourceWriter', () => {
{ {
path: 'output.stderr', path: 'output.stderr',
}, },
{
path: 'keepdir/file',
},
{
path: '.other/keepdir/.subdir/file',
},
] ]
ctx.resources = 'mock-resources' ctx.resources = 'mock-resources'
ctx.request = { ctx.request = {
@@ -309,18 +297,6 @@ describe('ResourceWriter', () => {
.should.equal(true) .should.equal(true)
}) })
it('should not delete additionally specified files', ctx => {
return ctx.ResourceWriter._deleteFileIfNotDirectory
.calledWith(path.join(ctx.basePath, 'keepdir/file'))
.should.equal(false)
})
it('should not delete additionally specified hidden files', ctx => {
return ctx.ResourceWriter._deleteFileIfNotDirectory
.calledWith(path.join(ctx.basePath, '.other/keepdir/.subdir/file'))
.should.equal(false)
})
it('should not delete the extra aux files', ctx => { it('should not delete the extra aux files', ctx => {
return ctx.ResourceWriter._deleteFileIfNotDirectory return ctx.ResourceWriter._deleteFileIfNotDirectory
.calledWith(path.join(ctx.basePath, 'extra.aux')) .calledWith(path.join(ctx.basePath, 'extra.aux'))
-2
View File
@@ -29,7 +29,6 @@ COPY libraries/object-persistor/package.json /overleaf/libraries/object-persisto
COPY libraries/promise-utils/package.json /overleaf/libraries/promise-utils/package.json COPY libraries/promise-utils/package.json /overleaf/libraries/promise-utils/package.json
COPY libraries/settings/package.json /overleaf/libraries/settings/package.json COPY libraries/settings/package.json /overleaf/libraries/settings/package.json
COPY libraries/stream-utils/package.json /overleaf/libraries/stream-utils/package.json COPY libraries/stream-utils/package.json /overleaf/libraries/stream-utils/package.json
COPY libraries/validation-tools/package.json /overleaf/libraries/validation-tools/package.json
COPY services/docstore/package.json /overleaf/services/docstore/package.json COPY services/docstore/package.json /overleaf/services/docstore/package.json
COPY tools/migrations/package.json /overleaf/tools/migrations/package.json COPY tools/migrations/package.json /overleaf/tools/migrations/package.json
COPY .yarn/patches/ /overleaf/.yarn/patches/ COPY .yarn/patches/ /overleaf/.yarn/patches/
@@ -46,7 +45,6 @@ COPY libraries/object-persistor/ /overleaf/libraries/object-persistor/
COPY libraries/promise-utils/ /overleaf/libraries/promise-utils/ COPY libraries/promise-utils/ /overleaf/libraries/promise-utils/
COPY libraries/settings/ /overleaf/libraries/settings/ COPY libraries/settings/ /overleaf/libraries/settings/
COPY libraries/stream-utils/ /overleaf/libraries/stream-utils/ COPY libraries/stream-utils/ /overleaf/libraries/stream-utils/
COPY libraries/validation-tools/ /overleaf/libraries/validation-tools/
COPY services/docstore/ /overleaf/services/docstore/ COPY services/docstore/ /overleaf/services/docstore/
COPY tools/migrations/ /overleaf/tools/migrations/ COPY tools/migrations/ /overleaf/tools/migrations/
+9 -7
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = docstore PROJECT_NAME = docstore
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -26,15 +26,17 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/libraries/promise-utils/package.json \ $(MONOREPO)/libraries/promise-utils/package.json \
$(MONOREPO)/libraries/settings/package.json \ $(MONOREPO)/libraries/settings/package.json \
$(MONOREPO)/libraries/stream-utils/package.json \ $(MONOREPO)/libraries/stream-utils/package.json \
$(MONOREPO)/libraries/validation-tools/package.json \
$(MONOREPO)/services/docstore/package.json \ $(MONOREPO)/services/docstore/package.json \
$(MONOREPO)/tools/migrations/package.json \ $(MONOREPO)/tools/migrations/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
+13 -2
View File
@@ -7,7 +7,11 @@ import Settings from '@overleaf/settings'
import logger from '@overleaf/logger' import logger from '@overleaf/logger'
import express from 'express' import express from 'express'
import bodyParser from 'body-parser' import bodyParser from 'body-parser'
import { handleValidationError } from '@overleaf/validation-tools' import {
celebrate as validate,
Joi,
errors as handleValidationErrors,
} from 'celebrate'
import mongodb from './app/js/mongodb.js' import mongodb from './app/js/mongodb.js'
import Errors from './app/js/Errors.js' import Errors from './app/js/Errors.js'
import HttpController from './app/js/HttpController.js' import HttpController from './app/js/HttpController.js'
@@ -75,6 +79,13 @@ app.post(
app.patch( app.patch(
'/project/:project_id/doc/:doc_id', '/project/:project_id/doc/:doc_id',
bodyParser.json(), bodyParser.json(),
validate({
body: {
deleted: Joi.boolean(),
name: Joi.string().when('deleted', { is: true, then: Joi.required() }),
deletedAt: Joi.date().when('deleted', { is: true, then: Joi.required() }),
},
}),
HttpController.patchDoc HttpController.patchDoc
) )
app.delete('/project/:project_id/doc/:doc_id', (req, res) => { app.delete('/project/:project_id/doc/:doc_id', (req, res) => {
@@ -90,7 +101,7 @@ app.get('/health_check', HttpController.healthCheck)
app.get('/status', (req, res) => res.send('docstore is alive')) app.get('/status', (req, res) => res.send('docstore is alive'))
app.use(handleValidationError) app.use(handleValidationErrors())
app.use(function (error, req, res, next) { app.use(function (error, req, res, next) {
if (error instanceof Errors.NotFoundError) { if (error instanceof Errors.NotFoundError) {
logger.warn({ req }, 'not found') logger.warn({ req }, 'not found')
+10 -12
View File
@@ -5,17 +5,6 @@ import HealthChecker from './HealthChecker.js'
import Errors from './Errors.js' import Errors from './Errors.js'
import Settings from '@overleaf/settings' import Settings from '@overleaf/settings'
import { expressify } from '@overleaf/promise-utils' import { expressify } from '@overleaf/promise-utils'
import { parseReq, z } from '@overleaf/validation-tools'
const patchDocSchema = z.object({
body: z
.object({
deleted: z.literal(true),
deletedAt: z.coerce.date(),
name: z.string(),
})
.strict(),
})
async function getDoc(req, res) { async function getDoc(req, res) {
const { doc_id: docId, project_id: projectId } = req.params const { doc_id: docId, project_id: projectId } = req.params
@@ -193,9 +182,18 @@ async function updateDoc(req, res) {
} }
async function patchDoc(req, res) { async function patchDoc(req, res) {
const { body: meta } = parseReq(req, patchDocSchema)
const { doc_id: docId, project_id: projectId } = req.params const { doc_id: docId, project_id: projectId } = req.params
logger.debug({ projectId, docId }, 'patching doc') logger.debug({ projectId, docId }, 'patching doc')
const allowedFields = ['deleted', 'deletedAt', 'name']
const meta = {}
Object.entries(req.body).forEach(([field, value]) => {
if (allowedFields.includes(field)) {
meta[field] = value
} else {
logger.fatal({ field }, 'joi validation for pathDoc is broken')
}
})
await DocManager.patchDoc(projectId, docId, meta) await DocManager.patchDoc(projectId, docId, meta)
res.sendStatus(204) res.sendStatus(204)
} }
+1
View File
@@ -5,6 +5,7 @@ docstore
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/docstore
--pipeline-owner=32 --pipeline-owner=32
--public-repo=True --public-repo=True
--test-unit-vitest=True --test-unit-vitest=True
+3 -3
View File
@@ -4,7 +4,7 @@
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/docstore/reports - ./reports:/overleaf/services/docstore/reports
@@ -23,7 +23,7 @@ services:
condition: service_started condition: service_started
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -49,7 +49,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
+1 -1
View File
@@ -25,10 +25,10 @@
"@overleaf/promise-utils": "workspace:*", "@overleaf/promise-utils": "workspace:*",
"@overleaf/settings": "workspace:*", "@overleaf/settings": "workspace:*",
"@overleaf/stream-utils": "^0.1.0", "@overleaf/stream-utils": "^0.1.0",
"@overleaf/validation-tools": "workspace:*",
"async": "^3.2.5", "async": "^3.2.5",
"body-parser": "1.20.4", "body-parser": "1.20.4",
"bunyan": "^1.8.15", "bunyan": "^1.8.15",
"celebrate": "^15.0.3",
"express": "4.22.1", "express": "4.22.1",
"lodash": "^4.18.1", "lodash": "^4.18.1",
"mongodb-legacy": "6.1.3", "mongodb-legacy": "6.1.3",
@@ -5,6 +5,6 @@ before('run migrations', async function () {
this.timeout(60_000) this.timeout(60_000)
await promisify(exec)( await promisify(exec)(
`cd ../../tools/migrations && yarn run migrations migrate -t 'server-ce & !nonblocking'` `cd ../../tools/migrations && npm run migrations -- migrate -t server-ce`
) )
}) })
@@ -486,11 +486,7 @@ describe('HttpController', () => {
project_id: ctx.projectId, project_id: ctx.projectId,
doc_id: ctx.docId, doc_id: ctx.docId,
} }
ctx.req.body = { ctx.req.body = { name: 'foo.tex' }
deleted: true,
deletedAt: '2026-06-15T00:00:00Z',
name: 'foo.tex',
}
ctx.DocManager.patchDoc = sinon.stub().resolves() ctx.DocManager.patchDoc = sinon.stub().resolves()
await ctx.HttpController.patchDoc(ctx.req, ctx.res, ctx.next) await ctx.HttpController.patchDoc(ctx.req, ctx.res, ctx.next)
}) })
@@ -498,12 +494,7 @@ describe('HttpController', () => {
it('should delete the document', ctx => { it('should delete the document', ctx => {
expect(ctx.DocManager.patchDoc).to.have.been.calledWith( expect(ctx.DocManager.patchDoc).to.have.been.calledWith(
ctx.projectId, ctx.projectId,
ctx.docId, ctx.docId
{
deleted: true,
deletedAt: new Date('2026-06-15T00:00:00Z'),
name: 'foo.tex',
}
) )
}) })
@@ -519,13 +510,19 @@ describe('HttpController', () => {
await ctx.HttpController.patchDoc(ctx.req, ctx.res, ctx.next) await ctx.HttpController.patchDoc(ctx.req, ctx.res, ctx.next)
}) })
it('should pass a validation error to next', ctx => { it('should log a message', ctx => {
expect(ctx.next).to.have.been.calledOnce expect(ctx.logger.fatal).to.have.been.calledWith(
expect(ctx.next.firstCall.args[0].name).to.equal('InvalidRequestError') { field: 'cannot' },
'joi validation for pathDoc is broken'
)
}) })
it('should not patch the document', ctx => { it('should not pass the invalid field along', ctx => {
expect(ctx.DocManager.patchDoc).not.to.have.been.called expect(ctx.DocManager.patchDoc).to.have.been.calledWith(
ctx.projectId,
ctx.docId,
{}
)
}) })
}) })
}) })
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = document-updater PROJECT_NAME = document-updater
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -31,10 +31,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/tools/migrations/package.json \ $(MONOREPO)/tools/migrations/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
@@ -5,5 +5,6 @@ document-updater
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/document-updater
--pipeline-owner=44 --pipeline-owner=44
--public-repo=True --public-repo=True
@@ -4,7 +4,7 @@
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/document-updater/reports - ./reports:/overleaf/services/document-updater/reports
@@ -28,7 +28,7 @@ services:
condition: service_healthy condition: service_healthy
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -56,7 +56,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
@@ -5,6 +5,6 @@ before('run migrations', async function () {
this.timeout(60_000) this.timeout(60_000)
await promisify(exec)( await promisify(exec)(
`cd ../../tools/migrations && yarn run migrations migrate -t 'server-ce & !nonblocking'` `cd ../../tools/migrations && npm run migrations -- migrate -t server-ce`
) )
}) })
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = filestore PROJECT_NAME = filestore
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -27,10 +27,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/services/filestore/package.json \ $(MONOREPO)/services/filestore/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
+1
View File
@@ -6,6 +6,7 @@ filestore
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/filestore
--pipeline-owner=32 --pipeline-owner=32
--public-repo=True --public-repo=True
--test-acceptance-shards=SHARD_01_,SHARD_02_,SHARD_03_ --test-acceptance-shards=SHARD_01_,SHARD_02_,SHARD_03_
+3 -3
View File
@@ -7,7 +7,7 @@ volumes:
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/filestore/reports - ./reports:/overleaf/services/filestore/reports
@@ -21,7 +21,7 @@ services:
VITEST_NO_CACHE: true VITEST_NO_CACHE: true
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -65,7 +65,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
+1 -1
View File
@@ -5,7 +5,7 @@ MVN_TARGET := target/writelatex-git-bridge-1.0-SNAPSHOT-jar-with-dependencies.ja
export BUILD_NUMBER ?= local export BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export COMMIT_SHA ?= $(shell git rev-parse HEAD) export COMMIT_SHA ?= $(shell git rev-parse HEAD)
PROJECT_NAME = git-bridge PROJECT_NAME = git-bridge
+1 -1
View File
@@ -38,7 +38,7 @@
<commons.compress.version>1.28.0</commons.compress.version> <commons.compress.version>1.28.0</commons.compress.version>
<simpleclient.version>0.10.0</simpleclient.version> <simpleclient.version>0.10.0</simpleclient.version>
<bouncycastle.crypto.version>1.84</bouncycastle.crypto.version> <bouncycastle.crypto.version>1.84</bouncycastle.crypto.version>
<netty.version>4.1.135.Final</netty.version> <netty.version>4.1.132.Final</netty.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = history-v1 PROJECT_NAME = history-v1
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -33,10 +33,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/tools/migrations/package.json \ $(MONOREPO)/tools/migrations/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
+1
View File
@@ -5,6 +5,7 @@ history-v1
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=overleaf-editor
--pipeline-owner=44 --pipeline-owner=44
--public-repo=False --public-repo=False
--tsconfig-extra-includes=backup-deletion-app.mjs,backup-verifier-app.mjs,backup-worker-app.mjs,api/**/*,migrations/**/*,storage/**/* --tsconfig-extra-includes=backup-deletion-app.mjs,backup-verifier-app.mjs,backup-worker-app.mjs,api/**/*,migrations/**/*,storage/**/*
+3 -3
View File
@@ -7,7 +7,7 @@ volumes:
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/history-v1/reports - ./reports:/overleaf/services/history-v1/reports
@@ -31,7 +31,7 @@ services:
condition: service_healthy condition: service_healthy
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -80,7 +80,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
+1 -1
View File
@@ -22,7 +22,7 @@ async function setupMongoDatabase() {
this.timeout(60_000) this.timeout(60_000)
await promisify(exec)( await promisify(exec)(
// Run saas migrations for backup indexes // Run saas migrations for backup indexes
`cd ../../tools/migrations && yarn run migrations migrate -t 'saas & !nonblocking'` `cd ../../tools/migrations && npm run migrations -- migrate -t saas`
) )
} }
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = notifications PROJECT_NAME = notifications
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -29,10 +29,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/tools/migrations/package.json \ $(MONOREPO)/tools/migrations/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
+1
View File
@@ -5,6 +5,7 @@ notifications
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/notifications
--pipeline-owner=32 --pipeline-owner=32
--public-repo=True --public-repo=True
--test-acceptance-vitest=True --test-acceptance-vitest=True
+3 -3
View File
@@ -4,7 +4,7 @@
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/notifications/reports - ./reports:/overleaf/services/notifications/reports
@@ -23,7 +23,7 @@ services:
condition: service_started condition: service_started
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -45,7 +45,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
@@ -4,6 +4,6 @@ import { promisify } from 'node:util'
beforeAll(async function () { beforeAll(async function () {
await promisify(exec)( await promisify(exec)(
`cd ../../tools/migrations && yarn run migrations migrate -t 'server-ce & !nonblocking'` `cd ../../tools/migrations && npm run migrations -- migrate -t server-ce`
) )
}, 60_000) }, 60_000)
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = project-history PROJECT_NAME = project-history
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -32,10 +32,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/tools/migrations/package.json \ $(MONOREPO)/tools/migrations/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
@@ -1,6 +1,5 @@
import Metrics from '@overleaf/metrics' import Metrics from '@overleaf/metrics'
import logger from '@overleaf/logger' import logger from '@overleaf/logger'
import OError from '@overleaf/o-error'
import express from 'express' import express from 'express'
import bodyParser from 'body-parser' import bodyParser from 'body-parser'
import * as Errors from './Errors.js' import * as Errors from './Errors.js'
@@ -55,13 +54,6 @@ app.use(function (error, req, res, next) {
res.sendStatus(422) res.sendStatus(422)
} else if (error instanceof Errors.TooManyRequestsError) { } else if (error instanceof Errors.TooManyRequestsError) {
res.status(429).set('Retry-After', 300).end() res.status(429).set('Retry-After', 300).end()
} else if (
error instanceof OError &&
error.message === 'Timeout' &&
error.info?.key
) {
logger.warn({ error, req }, error.message)
res.status(423).json({ message: 'redis lock is taken' })
} else { } else {
logger.error({ err: error, req }, error.message) logger.error({ err: error, req }, error.message)
res.status(500).json({ message: 'an internal error occurred' }) res.status(500).json({ message: 'an internal error occurred' })
+1
View File
@@ -5,5 +5,6 @@ project-history
--env-pass-through= --env-pass-through=
--esmock-loader=True --esmock-loader=True
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/project-history
--pipeline-owner=44 --pipeline-owner=44
--public-repo=False --public-repo=False
@@ -4,7 +4,7 @@
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/project-history/reports - ./reports:/overleaf/services/project-history/reports
@@ -28,7 +28,7 @@ services:
condition: service_healthy condition: service_healthy
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -56,7 +56,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
@@ -3,13 +3,10 @@ import { expect } from 'chai'
import { fetchNothing, fetchJsonWithResponse } from '@overleaf/fetch-utils' import { fetchNothing, fetchJsonWithResponse } from '@overleaf/fetch-utils'
import assert from 'node:assert' import assert from 'node:assert'
import mongodb from 'mongodb-legacy' import mongodb from 'mongodb-legacy'
import RedisWrapper from '@overleaf/redis-wrapper'
import * as ProjectHistoryClient from './helpers/ProjectHistoryClient.js' import * as ProjectHistoryClient from './helpers/ProjectHistoryClient.js'
import * as ProjectHistoryApp from './helpers/ProjectHistoryApp.js' import * as ProjectHistoryApp from './helpers/ProjectHistoryApp.js'
import Settings from '@overleaf/settings' import Settings from '@overleaf/settings'
const { ObjectId } = mongodb const { ObjectId } = mongodb
const LockKey = Settings.redis.lock.key_schema
const lockRClient = RedisWrapper.createClient(Settings.redis.lock)
const MockHistoryStore = () => nock('http://127.0.0.1:3100') const MockHistoryStore = () => nock('http://127.0.0.1:3100')
const MockWeb = () => nock('http://127.0.0.1:3000') const MockWeb = () => nock('http://127.0.0.1:3000')
@@ -153,22 +150,6 @@ describe('Flushing old queues', function () {
}) })
}) })
describe('when the project lock is already held', function () {
it('returns 423', async function () {
const key = LockKey.projectHistoryLock({ project_id: this.projectId })
await lockRClient.set(key, 'taken')
try {
const { statusCode } = await ProjectHistoryClient.flushProject(
this.projectId,
{ allowErrors: true }
)
expect(statusCode).to.equal(423)
} finally {
await lockRClient.del(key)
}
})
})
describe('when the update is newer than the cutoff and project has short queue', function () { describe('when the update is newer than the cutoff and project has short queue', function () {
beforeEach(function () { beforeEach(function () {
Settings.shortHistoryQueues.push(this.projectId) Settings.shortHistoryQueues.push(this.projectId)
@@ -5,6 +5,6 @@ before('run migrations', async function () {
this.timeout(60_000) this.timeout(60_000)
await promisify(exec)( await promisify(exec)(
`cd ../../tools/migrations && yarn run migrations migrate -t 'server-ce & !nonblocking'` `cd ../../tools/migrations && npm run migrations -- migrate -t server-ce`
) )
}) })
@@ -2,13 +2,7 @@ import sinon from 'sinon'
import { expect } from 'chai' import { expect } from 'chai'
import mongodb from 'mongodb-legacy' import mongodb from 'mongodb-legacy'
import tk from 'timekeeper' import tk from 'timekeeper'
import { import { File, Comment, TrackedChange, Range } from 'overleaf-editor-core'
File,
Comment,
TrackedChange,
Range,
TrackingProps,
} from 'overleaf-editor-core'
import { UnprocessableError } from 'overleaf-editor-core/lib/errors.js' import { UnprocessableError } from 'overleaf-editor-core/lib/errors.js'
import { strict as esmock } from 'esmock' import { strict as esmock } from 'esmock'
import { FileContentEmptyError } from '../../../../app/js/Errors.js' import { FileContentEmptyError } from '../../../../app/js/Errors.js'
@@ -2019,44 +2013,32 @@ describe('SyncManager', function () {
describe('syncing tracked changes', function () { describe('syncing tracked changes', function () {
beforeEach(function () { beforeEach(function () {
this.loadedSnapshotDoc.getTrackedChanges().add( this.loadedSnapshotDoc.getTrackedChanges().add(
new TrackedChange( new TrackedChange(new Range(4, 6), {
new Range(4, 6), type: 'delete',
TrackingProps.fromRaw({ userId: USER_ID,
type: 'delete', ts: new Date(TIMESTAMP),
userId: USER_ID, })
ts: new Date(TIMESTAMP).toISOString(),
})
)
) )
this.loadedSnapshotDoc.getTrackedChanges().add( this.loadedSnapshotDoc.getTrackedChanges().add(
new TrackedChange( new TrackedChange(new Range(10, 6), {
new Range(10, 6), type: 'insert',
TrackingProps.fromRaw({ userId: USER_ID,
type: 'insert', ts: new Date(TIMESTAMP),
userId: USER_ID, })
ts: new Date(TIMESTAMP).toISOString(),
})
)
) )
this.loadedSnapshotDoc.getTrackedChanges().add( this.loadedSnapshotDoc.getTrackedChanges().add(
new TrackedChange( new TrackedChange(new Range(20, 6), {
new Range(20, 6), type: 'delete',
TrackingProps.fromRaw({ userId: USER_ID,
type: 'delete', ts: new Date(TIMESTAMP),
userId: USER_ID, })
ts: new Date(TIMESTAMP).toISOString(),
})
)
) )
this.loadedSnapshotDoc.getTrackedChanges().add( this.loadedSnapshotDoc.getTrackedChanges().add(
new TrackedChange( new TrackedChange(new Range(40, 3), {
new Range(40, 3), type: 'insert',
TrackingProps.fromRaw({ userId: USER_ID,
type: 'insert', ts: new Date(TIMESTAMP),
userId: USER_ID, })
ts: new Date(TIMESTAMP).toISOString(),
})
)
) )
this.changes = [ this.changes = [
makeTrackedChange('td1', { p: 4, d: 'quick ' }), makeTrackedChange('td1', { p: 4, d: 'quick ' }),
+9 -6
View File
@@ -2,10 +2,10 @@
# Instead run bin/update_build_scripts from # Instead run bin/update_build_scripts from
# https://github.com/overleaf/internal/ # https://github.com/overleaf/internal/
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
export BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
export BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE = $(shell echo $(BRANCH_NAME) | sed 's/\//\-\-/g' | cut -c1-96)
export PROJECT_NAME = real-time PROJECT_NAME = real-time
BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]')
HERE=$(shell pwd) HERE=$(shell pwd)
export MONOREPO ?= $(shell cd ../../ && pwd) export MONOREPO ?= $(shell cd ../../ && pwd)
@@ -28,10 +28,13 @@ IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
$(MONOREPO)/services/real-time/package.json \ $(MONOREPO)/services/real-time/package.json \
$(MONOREPO)/.yarn/patches/* \ $(MONOREPO)/.yarn/patches/* \
| sha256sum | cut -d '-' -f1) | sha256sum | cut -d '-' -f1)
export MOCHA_GREP ?=
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
+1
View File
@@ -4,6 +4,7 @@ real-time
--env-pass-through= --env-pass-through=
--esmock-loader=False --esmock-loader=False
--node-version=24.14.1 --node-version=24.14.1
--package-name=@overleaf/real-time
--pipeline-owner=32 --pipeline-owner=32
--public-repo=False --public-repo=False
--test-unit-vitest=True --test-unit-vitest=True
+3 -3
View File
@@ -4,7 +4,7 @@
services: services:
test_unit: test_unit:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
user: node user: node
volumes: volumes:
- ./reports:/overleaf/services/real-time/reports - ./reports:/overleaf/services/real-time/reports
@@ -26,7 +26,7 @@ services:
condition: service_healthy condition: service_healthy
test_acceptance: test_acceptance:
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
environment: environment:
CI: CI:
RETRIES: RETRIES:
@@ -50,7 +50,7 @@ services:
tar: tar:
build: . build: .
image: ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
volumes: volumes:
- ./:/tmp/build/ - ./:/tmp/build/
command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs .
-1
View File
@@ -121,7 +121,6 @@ export default defineMain({
...storybookConfig.resolve?.alias, ...storybookConfig.resolve?.alias,
// custom prefixes for import paths // custom prefixes for import paths
'@': path.join(rootDir, 'frontend/js/'), '@': path.join(rootDir, 'frontend/js/'),
'@modules': path.join(rootDir, 'modules/'),
'@ol-types': path.join(rootDir, 'types/'), '@ol-types': path.join(rootDir, 'types/'),
'@ol-storybook': path.join(rootDir, '.storybook/'), '@ol-storybook': path.join(rootDir, '.storybook/'),
'@wf': path.join( '@wf': path.join(
+2 -2
View File
@@ -1,8 +1,8 @@
ARG PROJECT_NAME ARG PROJECT_NAME
ARG BRANCH_NAME_TAG_SAFE ARG BRANCH_NAME
ARG BUILD_NUMBER ARG BUILD_NUMBER
FROM ci/$PROJECT_NAME:$BRANCH_NAME_TAG_SAFE-$BUILD_NUMBER FROM ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER
USER root USER root
+10 -12
View File
@@ -1,11 +1,11 @@
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
export RETRIES ?= $(if $(CI),3,0) export RETRIES ?= $(if $(CI),3,0)
export BUILD_NUMBER ?= local BUILD_NUMBER ?= local
ifeq ($(BRANCH_NAME),) ifeq ($(BRANCH_NAME),)
export BRANCH_NAME := $(shell git rev-parse --abbrev-ref HEAD) export BRANCH_NAME := $(shell git rev-parse --abbrev-ref HEAD)
endif endif
export BRANCH_NAME_TAG_SAFE := $(shell echo "$(BRANCH_NAME)" | sed 's/\//\-\-/g' | cut -c1-96) BRANCH_NAME_TAG_SAFE := $(shell echo "$(BRANCH_NAME)" | sed 's/\//\-\-/g' | cut -c1-96)
.PHONY: print-branch-tag-safe .PHONY: print-branch-tag-safe
print-branch-tag-safe: print-branch-tag-safe:
@@ -14,7 +14,7 @@ print-branch-tag-safe:
ifeq ($(COMMIT_SHA),) ifeq ($(COMMIT_SHA),)
export COMMIT_SHA := $(shell git rev-parse HEAD) export COMMIT_SHA := $(shell git rev-parse HEAD)
endif endif
export PROJECT_NAME = web PROJECT_NAME = web
BUILD_DIR_NAME := web BUILD_DIR_NAME := web
PWD := $(shell pwd) PWD := $(shell pwd)
ifeq ($(MONOREPO),) ifeq ($(MONOREPO),)
@@ -28,8 +28,11 @@ CFG_SAAS=/overleaf/services/web/test/acceptance/config/settings.test.saas.js
CFG_SERVER_CE=/overleaf/services/web/test/acceptance/config/settings.test.server-ce.js CFG_SERVER_CE=/overleaf/services/web/test/acceptance/config/settings.test.server-ce.js
CFG_SERVER_PRO=/overleaf/services/web/test/acceptance/config/settings.test.server-pro.js CFG_SERVER_PRO=/overleaf/services/web/test/acceptance/config/settings.test.server-pro.js
export MOCHA_GREP ?= DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \
DOCKER_COMPOSE := docker compose ${DOCKER_COMPOSE_FLAGS} BRANCH_NAME=$(BRANCH_NAME_TAG_SAFE) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
# Common flags for docker compose run. # Common flags for docker compose run.
# --rm Tear down test runner container # --rm Tear down test runner container
@@ -151,7 +154,7 @@ MOCHA_CMD_LINE = \
mocha \ mocha \
--exit \ --exit \
--file test/unit/bootstrap.js \ --file test/unit/bootstrap.js \
--grep="${MOCHA_GREP}" \ --grep=${MOCHA_GREP} \
--reporter spec \ --reporter spec \
--timeout 25000 \ --timeout 25000 \
@@ -175,7 +178,7 @@ test_unit_module: mongo_migrations_for_tests
$(MAKE) modules/$(MODULE_NAME)/test_unit $(MAKE) modules/$(MODULE_NAME)/test_unit
mongo_migrations_for_tests: mongo_migrations_for_tests:
$(DOCKER_COMPOSE) run $(DC_RUN_FLAGS) --workdir /overleaf/tools/migrations test_unit yarn run migrations migrate -t 'saas & !nonblocking' $(DOCKER_COMPOSE) run $(DC_RUN_FLAGS) --workdir /overleaf/tools/migrations test_unit yarn run migrations migrate -t saas
# #
# Frontend tests # Frontend tests
@@ -547,10 +550,6 @@ else
$(RUN_LINTING) lint $(RUN_LINTING) lint
endif endif
lint_fix: lint_eslint_fix
lint_eslint_fix:
$(RUN_LINTING) lint:fix
lint: lint_stylelint lint: lint_stylelint
lint_stylelint: lint_stylelint:
ifeq ($(CI),true) ifeq ($(CI),true)
@@ -560,7 +559,6 @@ else
$(RUN_LINTING) lint:styles $(RUN_LINTING) lint:styles
endif endif
lint_fix: lint_stylelint_fix
lint_stylelint_fix: lint_stylelint_fix:
$(RUN_LINTING) lint:styles:fix $(RUN_LINTING) lint:styles:fix
+10 -9
View File
@@ -1,13 +1,8 @@
BUILD_DIR_NAME ?= web BUILD_DIR_NAME ?= web
ifeq ($(BRANCH_NAME),)
export BRANCH_NAME := $(shell git rev-parse --abbrev-ref HEAD)
endif
export BRANCH_NAME_TAG_SAFE := $(shell echo "$(BRANCH_NAME)" | sed 's/\//\-\-/g' | cut -c1-96)
export MODULE_NAME := $(notdir $(shell pwd)) export MODULE_NAME := $(notdir $(shell pwd))
export MODULE_DIR := modules/$(MODULE_NAME) MODULE_DIR := modules/$(MODULE_NAME)
export PROJECT_NAME = web PROJECT_NAME = web
export RETRIES ?= $(if $(CI),3,0) export RETRIES ?= $(if $(CI),3,0)
export MOCHA_GREP ?=
export OVERLEAF_CONFIG = /overleaf/services/web/$(MODULE_DIR)/test/acceptance/config/settings.test.js export OVERLEAF_CONFIG = /overleaf/services/web/$(MODULE_DIR)/test/acceptance/config/settings.test.js
export BASE_CONFIG ?= /overleaf/services/web/test/acceptance/config/settings.test.saas.js export BASE_CONFIG ?= /overleaf/services/web/test/acceptance/config/settings.test.saas.js
@@ -17,7 +12,13 @@ CFG_SERVER_CE=/overleaf/services/web/test/acceptance/config/settings.test.server
CFG_SERVER_PRO=/overleaf/services/web/test/acceptance/config/settings.test.server-pro.js CFG_SERVER_PRO=/overleaf/services/web/test/acceptance/config/settings.test.server-pro.js
DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml
DOCKER_COMPOSE := cd ../../ && docker compose ${DOCKER_COMPOSE_FLAGS} DOCKER_COMPOSE := cd ../../ && \
MODULE_DIR=$(MODULE_DIR) \
BUILD_NUMBER=$(BUILD_NUMBER) \
BRANCH_NAME=$(BRANCH_NAME) \
PROJECT_NAME=$(PROJECT_NAME) \
MOCHA_GREP=${MOCHA_GREP} \
docker compose ${DOCKER_COMPOSE_FLAGS}
ifeq (,$(wildcard test/unit)) ifeq (,$(wildcard test/unit))
test_unit: test_unit:
@@ -25,7 +26,7 @@ test_unit:
else else
test_unit: export COMPOSE_PROJECT_NAME=unit_test_$(BUILD_DIR_NAME)_$(MODULE_NAME) test_unit: export COMPOSE_PROJECT_NAME=unit_test_$(BUILD_DIR_NAME)_$(MODULE_NAME)
test_unit: test_unit:
${DOCKER_COMPOSE} run --rm test_unit yarn test:unit:run_dir ${MOCHA_ARGS} $(MODULE_DIR)/test/unit/src ${DOCKER_COMPOSE} run --rm test_unit yarn test:unit:run_dir -- ${MOCHA_ARGS} $(MODULE_DIR)/test/unit/src
${DOCKER_COMPOSE} down ${DOCKER_COMPOSE} down
endif endif
@@ -82,7 +82,7 @@ async function recordEventForUser(userId, event, segmentation) {
userId, userId,
event, event,
segmentation, segmentation,
isLabsUser: labsProgram, isLabsUser: Boolean(labsProgram),
isLoggedIn: true, isLoggedIn: true,
}) })
} }
@@ -97,31 +97,6 @@ function recordEventForUserInBackground(userId, event, segmentation) {
}) })
} }
async function recordEventForMongoUser(user, event, segmentation) {
const { userId, analyticsId, labsProgram } = _getIdsFromMongoUser(user)
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
return
}
_recordEvent({
analyticsId,
userId,
event,
segmentation,
isLabsUser: labsProgram,
isLoggedIn: true,
})
}
function recordEventForMongoUserInBackground(user, event, segmentation) {
const { userId } = _getIdsFromMongoUser(user) // throw before going into the background.
recordEventForMongoUser(user, event, segmentation).catch(err => {
logger.warn(
{ err, userId, event, segmentation },
'failed to record event for user'
)
})
}
function recordEventForSession(session, event, segmentation) { function recordEventForSession(session, event, segmentation) {
const { analyticsId, userId } = getIdsFromSession(session) const { analyticsId, userId } = getIdsFromSession(session)
if (!analyticsId) { if (!analyticsId) {
@@ -167,7 +142,7 @@ async function setUserPropertyForUser(userId, propertyName, propertyValue) {
if (analyticsId) { if (analyticsId) {
await _setUserProperty({ await _setUserProperty({
analyticsId, analyticsId,
isLabsUser: labsProgram, isLabsUser: Boolean(labsProgram),
propertyName, propertyName,
propertyValue, propertyValue,
}) })
@@ -183,41 +158,6 @@ function setUserPropertyForUserInBackground(userId, property, value) {
}) })
} }
/**
* @param {{_id: ObjectId, analyticsId: string, labsProgram: boolean}} user
* @param {string} propertyName
* @param {any} propertyValue
* @return {Promise<void>}
*/
async function setUserPropertyForMongoUser(user, propertyName, propertyValue) {
const { userId, analyticsId, labsProgram } = _getIdsFromMongoUser(user)
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
return
}
_checkPropertyValue(propertyValue)
await _setUserProperty({
analyticsId,
isLabsUser: labsProgram,
propertyName,
propertyValue,
})
}
/**
* @param {{_id: ObjectId, analyticsId: string, labsProgram: boolean}} user
* @param {string} property
* @param {any} value
*/
function setUserPropertyForMongoUserInBackground(user, property, value) {
const { userId } = _getIdsFromMongoUser(user) // throw before going into the background.
setUserPropertyForMongoUser(user, property, value).catch(err => {
logger.warn(
{ err, userId, property, value },
'failed to set user property for user'
)
})
}
async function setUserPropertyForAnalyticsId( async function setUserPropertyForAnalyticsId(
analyticsId, analyticsId,
propertyName, propertyName,
@@ -252,8 +192,8 @@ async function setUserPropertyForSession(session, propertyName, propertyValue) {
} }
function setUserPropertyForSessionInBackground(session, property, value) { function setUserPropertyForSessionInBackground(session, property, value) {
const { analyticsId, userId } = getIdsFromSession(session) // throw before going into the background.
setUserPropertyForSession(session, property, value).catch(err => { setUserPropertyForSession(session, property, value).catch(err => {
const { analyticsId, userId } = getIdsFromSession(session)
logger.warn( logger.warn(
{ err, analyticsId, userId, property, value }, { err, analyticsId, userId, property, value },
'failed to set user property for session' 'failed to set user property for session'
@@ -512,26 +452,6 @@ function _isAnalyticsDisabled() {
return !(Settings.analytics && Settings.analytics.enabled) return !(Settings.analytics && Settings.analytics.enabled)
} }
/**
* @param {{_id: ObjectId, analyticsId: string, labsProgram: boolean}} user
* @return {{userId: string, analyticsId: string, labsProgram: boolean}}
*/
function _getIdsFromMongoUser(user) {
const userId = user?._id?.toString()
if (!userId) {
throw new Error('bug: include db.users._id in projection')
}
const analyticsId = user?.analyticsId
if (!analyticsId) {
throw new Error('bug: include db.users.analyticsId in projection')
}
const labsProgram = user?.labsProgram
if (typeof labsProgram !== 'boolean') {
throw new Error('bug: include db.users.labsProgram in projection')
}
return { userId, analyticsId, labsProgram }
}
function _checkPropertyValue(propertyValue) { function _checkPropertyValue(propertyValue) {
if (propertyValue === undefined) { if (propertyValue === undefined) {
throw new Error( throw new Error(
@@ -578,15 +498,13 @@ async function analyticsIdMiddleware(req, res, next) {
if (sessionUser) { if (sessionUser) {
// For old sessions, session.analyticsId is the anon id immediately after login. Do not use it! // For old sessions, session.analyticsId is the anon id immediately after login. Do not use it!
session.analyticsId = sessionUser.analyticsId session.analyticsId = sessionUser.analyticsId
if (!session.analyticsId || typeof sessionUser.labsProgram !== 'boolean') { if (!session.analyticsId) {
const { analyticsId, labsProgram } = session.analyticsId = sessionUser.analyticsId =
await UserAnalyticsDataCache.getAnalyticsData( await UserAnalyticsDataCache.getAnalyticsId(
sessionUser._id, sessionUser._id,
// Do not drill down further, this middleware is on all endpoints. // Do not drill down further, this middleware is on all endpoints.
'analyticsIdMiddleware' 'analyticsIdMiddleware'
) )
session.analyticsId = sessionUser.analyticsId = analyticsId
sessionUser.labsProgram = labsProgram
} }
} else if (!session.analyticsId) { } else if (!session.analyticsId) {
// generate an `analyticsId` if needed // generate an `analyticsId` if needed
@@ -603,13 +521,9 @@ export default {
recordEventForSession, recordEventForSession,
recordEventForUser, recordEventForUser,
recordEventForUserInBackground, recordEventForUserInBackground,
recordEventForMongoUser,
recordEventForMongoUserInBackground,
emitPackageUsage, emitPackageUsage,
setUserPropertyForUser, setUserPropertyForUser,
setUserPropertyForUserInBackground, setUserPropertyForUserInBackground,
setUserPropertyForMongoUser,
setUserPropertyForMongoUserInBackground,
setUserPropertyForSession, setUserPropertyForSession,
setUserPropertyForSessionInBackground, setUserPropertyForSessionInBackground,
setUserPropertyForAnalyticsId, setUserPropertyForAnalyticsId,
@@ -25,32 +25,32 @@ function clearInbound(session) {
} }
} }
function addUserProperties(user, session) { function addUserProperties(userId, session) {
if (!session) { if (!session) {
return return
} }
if (session.required_login_from_product_medium) { if (session.required_login_from_product_medium) {
AnalyticsManager.setUserPropertyForMongoUserInBackground( AnalyticsManager.setUserPropertyForUserInBackground(
user, userId,
`registered-from-product-medium`, `registered-from-product-medium`,
session.required_login_from_product_medium session.required_login_from_product_medium
) )
if (session.required_login_from_product_source) { if (session.required_login_from_product_source) {
AnalyticsManager.setUserPropertyForMongoUserInBackground( AnalyticsManager.setUserPropertyForUserInBackground(
user, userId,
`registered-from-product-source`, `registered-from-product-source`,
session.required_login_from_product_source session.required_login_from_product_source
) )
} }
} else if (session.referal_id) { } else if (session.referal_id) {
AnalyticsManager.setUserPropertyForMongoUserInBackground( AnalyticsManager.setUserPropertyForUserInBackground(
user, userId,
`registered-from-bonus-scheme`, `registered-from-bonus-scheme`,
true true
) )
AnalyticsManager.setUserPropertyForMongoUserInBackground( AnalyticsManager.setUserPropertyForUserInBackground(
user, userId,
`registered-from-product-medium`, `registered-from-product-medium`,
'bonus-scheme' 'bonus-scheme'
) )
@@ -58,16 +58,16 @@ function addUserProperties(user, session) {
if (session.inbound) { if (session.inbound) {
if (session.inbound.referrer && session.inbound.referrer.medium) { if (session.inbound.referrer && session.inbound.referrer.medium) {
AnalyticsManager.setUserPropertyForMongoUserInBackground( AnalyticsManager.setUserPropertyForUserInBackground(
user, userId,
`registered-from-referrer-medium`, `registered-from-referrer-medium`,
`${session.inbound.referrer.medium `${session.inbound.referrer.medium
.charAt(0) .charAt(0)
.toUpperCase()}${session.inbound.referrer.medium.slice(1)}` .toUpperCase()}${session.inbound.referrer.medium.slice(1)}`
) )
if (session.inbound.referrer.source) { if (session.inbound.referrer.source) {
AnalyticsManager.setUserPropertyForMongoUserInBackground( AnalyticsManager.setUserPropertyForUserInBackground(
user, userId,
`registered-from-referrer-source`, `registered-from-referrer-source`,
session.inbound.referrer.source session.inbound.referrer.source
) )
@@ -77,8 +77,8 @@ function addUserProperties(user, session) {
if (session.inbound.utm) { if (session.inbound.utm) {
for (const utmKey of RequestHelper.REGISTRATION_UTM_KEYS) { for (const utmKey of RequestHelper.REGISTRATION_UTM_KEYS) {
if (session.inbound.utm[utmKey]) { if (session.inbound.utm[utmKey]) {
AnalyticsManager.setUserPropertyForMongoUserInBackground( AnalyticsManager.setUserPropertyForUserInBackground(
user, userId,
`registered-from-${utmKey.replace('_', '-')}`, `registered-from-${utmKey.replace('_', '-')}`,
session.inbound.utm[utmKey] session.inbound.utm[utmKey]
) )
@@ -685,7 +685,7 @@ function _loginAsyncHandlers(req, user, anonymousAnalyticsId, isNewUser) {
LoginRateLimiter.recordSuccessfulLogin(user.email, () => {}) LoginRateLimiter.recordSuccessfulLogin(user.email, () => {})
AuthenticationController._recordSuccessfulLogin(user._id, () => {}) AuthenticationController._recordSuccessfulLogin(user._id, () => {})
AuthenticationController.ipMatchCheck(req, user) AuthenticationController.ipMatchCheck(req, user)
Analytics.recordEventForMongoUserInBackground(user, 'user-logged-in', { Analytics.recordEventForUserInBackground(user._id, 'user-logged-in', {
source: req.session.saml source: req.session.saml
? 'saml' ? 'saml'
: req.user_info?.auth_provider || 'email-password', : req.user_info?.auth_provider || 'email-password',
@@ -8,7 +8,7 @@ import { expressify } from '@overleaf/promise-utils'
async function optIn(req, res) { async function optIn(req, res) {
const userId = SessionManager.getLoggedInUserId(req.session) const userId = SessionManager.getLoggedInUserId(req.session)
await BetaProgramHandler.promises.optIn(req.session, userId) await BetaProgramHandler.promises.optIn(userId)
try { try {
await SplitTestSessionHandler.promises.sessionMaintenance(req, null) await SplitTestSessionHandler.promises.sessionMaintenance(req, null)
} catch (error) { } catch (error) {
@@ -22,7 +22,7 @@ async function optIn(req, res) {
async function optOut(req, res) { async function optOut(req, res) {
const userId = SessionManager.getLoggedInUserId(req.session) const userId = SessionManager.getLoggedInUserId(req.session)
await BetaProgramHandler.promises.optOut(req.session, userId) await BetaProgramHandler.promises.optOut(userId)
try { try {
await SplitTestSessionHandler.promises.sessionMaintenance(req, null) await SplitTestSessionHandler.promises.sessionMaintenance(req, null)
} catch (error) { } catch (error) {

Some files were not shown because too many files have changed in this diff Show More