Commit Graph
2596 Commits
Author SHA1 Message Date
3341d5b217 🔧(renovate) use the suitenumerique/ci default preset
Replace the numerique-gouv/renovate-configuration preset with
github>suitenumerique/ci//renovate/default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ZTuvFw5C5t3xxzCmb3cEp
2026-10-14 09:35:08 +02:00
Manuel Raynaud 952ff704f2 ⚡️(yhub) skip the users/me call for callers without a session cookie
After removing the ForceSessionMiddleware we can detect without calling
the /users/me endpoint if a user is anonymous or not. We now check the
presence of the cookie session to avoid the http call and directly
return an anonymous user.
2026-10-13 15:23:38 +02:00
Manuel Raynaud ecd2d93cc2 🔥(backend) remove ForceSessionMiddleware
This middleware was introduced in 651f2d1d to create a session for every
user — connected to the collaboration websocket or not — so the backend
could arbitrate who may save a document (websocket payload flag and save
lock). The whole safeguard was removed in 68f5f5cdf when the content and
its permissions moved to yhub, leaving the middleware orphaned: nothing
reads an anonymous session anymore (the OIDC login flow saves its own
session, CSRF is cookie-based, throttling is not session-keyed).

It only kept a cost: a Redis write plus a Set-Cookie on every anonymous
response, which had already required two bugfix commits to exempt the
probes. A regression test pins the probe invariant going forward.
2026-10-13 15:23:37 +02:00
Ovgodd 7b8ebd1374 ♿️(frontend) keep the doc tree focus and its accessible structure
The sub pages exposed a nested tree role. Opening a document remounted
the tree, which took the focus back from the title.
2026-10-13 13:38:03 +02:00
Ovgodd aaa69c2991 ♿️(frontend) announce the doc options button as a menu button
The options button was not exposed as a menu button to assistive tech.
2026-10-13 13:37:25 +02:00
Ovgodd 53f0d54d90 ♿️(frontend) keep the focus on the doc options button
Starring a sub page remounted the tree row and dropped the focus.
2026-10-13 13:37:25 +02:00
Anthony LC 918f67ac5e 🐛(frontend) stop the http fallback when the server refuses it
Where websockets are blocked, a tab whose access was revoked or whose
session expired took a 403 on every PATCH, forever, since only the
websocket's 4401 close made the tab fetch the document again. Treat a
401, 403 or 404 on the fallback the same way: stop both transports and
fetch the document again.
2026-10-13 11:31:20 +02:00
Anthony LC 693837a31e ✨(backend) add a setting to turn off awareness on the http fallback
An editor on the http fallback publishes its presence every 10 seconds,
a request on its own, and the library then never polls less often than
every 15 seconds. On a deployment where many browsers cannot open the
websocket, this is most of the traffic and each request is authorized
against the backend.

Add COLLABORATION_HTTP_FALLBACK_AWARENESS_ENABLED (default True, so
nothing changes unless it is set), served by the config endpoint. Off,
an editor sends a request only when it has changes and
COLLABORATION_HTTP_FALLBACK_SYNC_INTERVAL applies in full. The cost is
no remote cursors on the fallback.

Signed-off-by: Anthony LC <anthony.le-courric@mail.numerique.gouv.fr>
2026-10-13 11:31:20 +02:00
Anthony LC abfb7157d2 ✨(backend) add a setting for the http fallback polling interval
Each poll of a browser on the http fallback is authorized against the
backend, and the 10 second interval was hardcoded. Add
COLLABORATION_HTTP_FALLBACK_SYNC_INTERVAL (milliseconds, default 10000,
so nothing changes unless it is set), served by the config endpoint, so
operators can lower that load without a new release.
2026-10-13 11:31:20 +02:00
Dor ZairiandAnthony LC 9a6849110b 🐛(frontend) keep the loading skeleton until an empty doc has synced
A doc never opened on this device has an empty local copy, and the
socket opening is not the content arriving either. Rendering on either
showed the empty-doc placeholder for a moment, which reads as a doc whose
content was deleted.

Signed-off-by: Dor Zairi <dorzairidev@gmail.com>
2026-10-13 10:26:26 +02:00
Anthony LC c5f97ab685 🐛(frontend) switch language when the current one is untranslated
With zh-tw active, selecting English did nothing: its bundle is empty, so
i18next reports `resolvedLanguage` as en while `language` is zh-tw, and
the hook skipped the change. Compare with `language` instead, and add
unit tests on the language pickers.
2026-10-13 08:56:10 +02:00
Manuel RaynaudandGitHub e28ba1ec7b 🔧(helm) allow to set the period and failure threshold of probes
The persiodSeconds and failureThreshold parameters for the probes are
not configurable. To allow to set them, the helper now will use them if
defined.
2026-10-12 20:04:29 +00:00
Manuel RaynaudandGitHub 80578179fe 🐛(backend) bypass throttling on access/me action from yhub
A throttling bypass exists on the DocumentThrottle class allowing YHub
server to fetch django and not be blocked by the throttling. YHub also
fetch an endpont on the DocumentAccess viewset and this bypass is not
existing. We must add it. We factorized the bypass in a mixin and this
mixin is now used by both the DocumentThrottle and
DocumentAccessThrottle classes.
2026-10-12 17:33:24 +02:00
Manuel Raynaud dbf8fece4e 🔖(release) major 6.0.0
Added

- 👷(ci) lint, typecheck, build and test the collaboration server (yhub) #2757
- 👷(ci) check the load-test tooling: swarm, canary, k6 and dashboards #2757
- 👷(ci) cancel the superseded runs of a pull request #2757
- ✨(backend) add mention endpoint with cooldown-limited email
  notification #2447
- 🚩(setting) add feature flag on Duplicate with Children #2721
- 💄(frontend) redesign 404 error standalone page #2696
- 💄(frontend) redesign 403 access denied page #2720
- ✨(frontend) duplicate with subdocuments #2584
- ✨(frontend) add markdown download option #2608
- ✨(frontend) turn pasted doc links into interlinks #2713
- ✨(helm) allow disallowing search engine indexing per instance #2694
- ✨(backend) expose the attachment max size in the config endpoint #2577
- ✨(frontend) warn before uploading an attachment over the size limit #2577
- ✨(loadtest) add a websocket load generator for the collaboration
  server #2556
- ✨(loadtest) add k6 scenarios for the page-open sequence and the heavy
  endpoints #2556
- ✨(loadtest) add browser canaries measuring what a user feels under
  load #2556
- ✨(loadtest) add the grafana dashboards of the load-test campaign, valkey
  included #2556
- 📝(documentation) add the load-testing guide #2556
- 🔧(helm) run grafana with those dashboards in the dev cluster, in place of
  the prometheus console #2556
- ✨(backend) measure the calls to yhub and to the converters, the database
  pool and the celery queue #2556
- ✨(backend) add a `LoadTest` configuration and its `loadtest` application,
  minting sessions for load tests #2556
- ✨(collaboration) add opt-in prometheus metrics to yhub, server and worker,
  protected by a bearer token #2556
- ✨(collaboration) report yhub errors to sentry, configured through
  `SENTRY_*` #2556
- ✨(backend) add opt-in prometheus metrics on `/metrics`, protected by a
  bearer token #2556
- ✨(helm) add a dedicated ingress for the prometheus metrics of the backend
  and of yhub #2556
- ✨(helm) add a ServiceMonitor and a PodMonitor per component whose metrics
  are enabled, and `backend.metrics.enabled` #2556
- 🔧(helm) scrape the metrics of the dev cluster with a trimmed
  kube-prometheus-stack and the ServiceMonitors of the chart #2556
- 🔧(helm) serve the example console of django-prometheus on the dev
  Prometheus #2556
- ✨(backend) add a service generating cached RS256 JWT tokens #2556
- ✨(backend) publish the JWT public key on a JWKS endpoint #2556
- 🔧(dev) generate the JWT signing key when bootstrapping the dev stack #2556
- ✨(collaboration) add an admin reset-connections endpoint on yhub #2556
- ✨(collaboration) add a create-ydoc endpoint on yhub #2556
- ✨(collaboration) soft-migrate legacy S3 documents into yhub #2556
- ✨(collaboration) replay legacy s3 version history into yhub #2556
- ✨(backend) add a service to call the yhub REST API #2556
- ✨(backend) call YHubService to seed initial document content #2556
- ✨(collaboration) add a get-ydoc endpoint on yhub #2556
- ✨(backend) duplicate a document through the collaboration server #2556
- ✨(backend) serve `documents/{id}/formatted-content/` from yhub #2556
- ✨(collaboration) notify the backend when the worker persists new
  content #2556
- ✨(collaboration) let a user read the document's editing history #2556
- ✨(frontend) fall back to http polling when the websocket cannot be
  opened. #2556
- 🔧(collaboration) make the version-history granularity configurable through
  `COLLABORATION_VERSION_GRANULARITY_MS` #2556
- ✨(frontend) keep a local copy of documents, so they open and stay editable
  offline #2556

Changed

- 🛂(backend) let users allowed to comment list each other's accesses #2447
- ♻️(collaboration) migrate the collaboration server from hocuspocus to
  yhub #2556
- 💥(y-provider) y-provider becomes converter-only #2556
- 💥(backend) move the resource server JWKS from `/api/{version}/jwks` to
  `/external_api/{version}/jwks` #2556
- 🔧(collaboration) adapt docker stack for development purpose #2556
- 🔧(helm) run a valkey for the backend and one for yhub in dev and
  feature #2556
- 💄(frontend) redesign 401 error standalone page #2716

Fixed

- 🐛(documentation) fix minio port and keycloak realm in the k8s guide #2751
- 🐛(collaboration) bound the calls yhub makes to the backend with a timeout,
  `YHUB_BACKEND_REQUEST_TIMEOUT_MS`, 5s by default #2753
- 🐛(backend) compensate document duplication failures #2755
- 🐛(frontend) open search results in a new tab with ctrl/cmd+click #2719
- 🐛(docker) pull minio images from pgsty #2739
- 🐛(backend) retry the duplicate of a document on a tree path collision #2726
- 🐛(frontend) keep caption and alignment when replacing an image #2730
- 🐛(backend) reset the collaboration connections on every access change #2725
- ⚡️(backend) coalesce and pace the resets of collaboration connections #2725
- 🐛(frontend) clear callout background on Backspace #2052
- 🐛(export) keep image aspect ratio in PDF columns #2670
- 🐛(frontend) fix redirect after deleting a document #2706
- 🐛(frontend) keep documents draggable with a mouse when zoomed #2727
- 📝(docs) fix markdown typo in installation README #2766
- 🐛(i18n) export locale correctly #2750
- 🐛(frontend) keep login and logout out of the service worker cache #2771
- 🐛(frontend) reduce PostHog volume from web vitals and opt_in spam #2701
- 🐛(frontend) stop reconnecting to the websocket based on the status
  code #2556
- 🐛(frontend) stop the service worker from caching the collaboration server's
  rest api #2556

Removed

- 🔥(backend) remove the unused `CollaborationService` #2556
- 💥(backend) remove the `documents/{id}/can-edit/` endpoint #2556
- 💥(backend) remove the `documents/{id}/content/` endpoint #2556
- 🔥(backend) remove the document version endpoints. #2556
- 🔥(backend) remove `Document.content` #2556

Security

- 🔒️(collaboration) stop read-only users from sharing their cursor #2556
v6.0.0-preprod v6.0.0
2026-10-09 11:14:07 +02:00
Manuel Raynaud 2674bdf064 ♻️(backend) only report restoration of a document to yhub
the task `sync_service_deletions_in_cascade` is no more called when a
document is soft deleted. We rename it in
sync_service_restorations_in_cascade and still call it when a document
is restored. No ydoc should be tombstone in yhub anymore but we want to
keep it as a safe guard, it is still possible to tombstone a ydoc
programmatically but not used in the application for now.
2026-10-09 11:14:07 +02:00
Manuel Raynaud b7450559ad 🔒️(backend) reset connection when a document is deleted
When a document is deleted, we must reset the connection in the
collaboration server otherwise all users remain connected and can still
edit it if they are at least editor.
2026-10-09 11:14:07 +02:00
Manuel Raynaud aaa3176fee ⏪️(backend) stop deleting document in yhub
One feature we have is to still be able to read a soft deleted document
in the application. Calling the yhub service to delete the document
inside yhub make it unreachable, so it does not load anymore in the
editor. Yhub map the permission on what Docs returns in its abilities.
A soft deleted document will be in read only.
2026-10-09 11:14:06 +02:00
Manuel Raynaud e80751d157 🔒️(backend) fix race conditions between actions updating a docs
If two requests at the exact same time update a document and
link-configuration, a race condition can happen and one action can
overwrite the other one. If a document link_reach is changed from public
to restricted, this change can be overwritten and the owner does not
have this information. To fix this issue we set the updated_fields for
every save.
2026-10-09 11:14:06 +02:00
Anthony LCandManuel Raynaud 953dded12a 🐛(frontend) never list two versions ending at the same moment
A version is identified by the moment it ends, which is also all the
changeset and the rollback are given. Two entries of the timeline can end
at the same moment without merging: an imported `system` save next to an
edit, or concurrent edits sorted by start. They produced two versions
with the same id, which both looked selected and shared a React key.
Entries ending together are the same document, so they are now folded
into one version, keeping every author and the earliest start.

Signed-off-by: Anthony LC <anthony.le-courric@mail.numerique.gouv.fr>
2026-10-09 11:14:06 +02:00
Manuel Raynaud a6838aec2b ⬆️(frontend) upgrade nginx to version 1.31.6
We need to upgrade to the last version of nginx to fix recent security
issues.
2026-10-09 11:14:05 +02:00
Manuel Raynaud bbeca02cf1 ✅(backend) fix translations in tests
Some translation have changed, the tests related to this string must be
updated accordingly.
2026-10-09 11:14:05 +02:00
Manuel Raynaud 0d0cac0c18 🚨(yhub) ignore some not critical CVE
Trivy is reporting some CVE from debian packages and they have not
published patches. These CVE are not critical, we ignore them for now.
2026-10-09 11:14:05 +02:00
Manuel Raynaud 0bffa5a3ee 🔧(dockerfile) switch yhub image to slim image
The yhub image was using a normal debian images with to many
dependencies installed that can contain CVE. Using a slim image is
enough
2026-10-09 11:14:05 +02:00
Manuel Raynaud ba36bf27fa 🔧(dockerfile) remove pip from backend image
pip is not used in production, we can remove it in the production to
avoid having to maintain library linked to it and that can possibly
contains CVEs
2026-10-09 11:14:04 +02:00
Manuel Raynaud 8fadf282f2 ⬆️(dependencies) update yhub-server dependencies 2026-10-09 11:14:04 +02:00
Manuel Raynaud 7b1c955343 ⬆️(dependencies) update frontend dependencies 2026-10-09 11:14:04 +02:00
Manuel Raynaud 9a794bbd75 ⬆️(dependencies) update python dependencies 2026-10-09 11:14:03 +02:00
lunikaandManuel Raynaud d1eb250130 🌐(i18n) update translated strings
Update translated files with new translations
2026-10-05 17:29:38 +02:00
Anthony LC 6e5dee38e9 🐛(frontend) cache hashed next static assets and revalidate html
Files under /_next/static/ carry a content hash in their name but were
served without Cache-Control, so browsers fell back to heuristic freshness
and revalidated each of the ~45 files of a page with a conditional request.
Serve them with a one-year immutable lifetime.

HTML pages, service-worker.js and public files keep a stable name: send
Cache-Control: no-cache so the browser revalidates them and always gets
the chunk list and service worker of the current release. The headers are
repeated in every location, as nginx drops the inherited add_header
directives as soon as a location defines its own.

Closes #2770

Signed-off-by: Anthony LC <anthony.le-courric@mail.numerique.gouv.fr>
2026-10-05 16:34:08 +02:00
Manuel Raynaud 2bd30312cd ⚡️(backend) coalesce and pace the resets of collaboration connections
For big documents, calling yhub for each sub documents can be a big
amount of request at the same time. We decided to group reset by batch
in the path order and call again the reset celery task with a delay to
continue.

Fix #2656
2026-10-05 14:15:07 +02:00
Manuel Raynaud 64338d55d0 🐛(backend) reset the collaboration connections on every access change
Reset to the collaboration server when an access is changed was
partially made. There is still action in the application that does not
cover this like when a document is moved or soft deleted.
Instead of doing the reset in the viewset and searching for every action
it should be made, we are making it now in the model to miss new or
modified path acting on an access. For detecting when a bulk
update/delete is made on the access model, we use existing
signal.
2026-10-05 14:15:06 +02:00
Roee SivanandAnthony LC 1983951843 🐛(frontend) keep login and logout out of the service worker cache
The login and logout navigations (authenticate/, callback/, logout/,
logout-callback/) reach the service worker on the same origin, and the
catch-all api route cached their redirects. When the network step
failed, the worker answered authenticate/ with a cached redirect that
carried an OIDC state already used, so the login failed: a 400 on the
callback before django-lasuite 0.0.30.

Give these urls their own NetworkOnly route, registered before the
catch-all one, so they never read or write the cache. ApiPlugin stays
on it, so the offline mutations are still replayed before a logout
ends the session.

Fixes #2477

Signed-off-by: Roee Sivan <roee.sivan@post.runi.ac.il>
2026-10-05 11:21:37 +02:00
renovate[bot]andGitHub 5d38f95e8d ⬆️(dependencies) update @ai-sdk/openai to v3.0.119 2026-10-05 01:06:19 +00:00
Anthony LC 861065301a ⬆️(dependency) upgrade lib0 from 0.2.114 to 0.2.119
Snyk has created this PR to upgrade lib0 from 0.2.114 to 0.2.119.

See this package in yarn:
lib0
2026-10-02 17:56:16 +02:00
Anthony LC 03838c14d4 ✅(e2e) improve e2e sub-page handling
A test was getting flaky, and was blocking the CI pipeline.
This change improves the handling of sub-pages in
the e2e tests to make them more reliable.
2026-10-02 15:59:13 +02:00
Thomas RaméandAnthony LC 5adfdee0d3 🐛(dev) missing default network for podman compose usage
Some services, like celery-dev, attach to the `default` network
explicitly, next to `lasuite`. Docker compose creates that implicit
network on its own. Podman compose does not when the top-level
`networks` section is declared without it, so `compose up` failed on
the unknown `default` network.

Declare `default: {}` so that the network exists with both tools.
2026-10-02 15:44:34 +02:00
Anthony LC 925ececab6 🐛(frontend) select a single language in the language picker
The language picker marked an option as selected when it matched the
current language, falling back to a match on the language alone. With the
current language zh-tw, the zh-cn option matched too, so several regions
of a language were selected at once, and the legacy picker could show the
label of the wrong one. Now that zh-cn and zh-tw are separate frontend
translations, this shows up for the Chinese locales, and it would for any
language with several regions (en-us/en-gb, pt-pt/pt-br).

Add getClosestLocale, which returns the one available locale closest to
the current language (the exact match first, then the first region of the
language), and use it in both pickers to select the option and to display
the current label.
2026-10-02 12:12:53 +02:00
Anthony LC c39c51a756 🌐(i18n) update translated strings
Pull the translations from Crowdin with the new locale-based export, so
that the frontend translations reflect the new taxonomy: zh-CN and zh-TW
are now two distinct entries (zh-cn and zh-tw) in translations.json
instead of a single "zh" one, and the backend .po files are refreshed.
2026-10-02 12:12:52 +02:00
Anthony LC da95cce985 🌐(i18n) key crowdin frontend exports by locale
Crowdin exported the frontend translations to a folder named after
%two_letters_code%, so every regional variant of a language landed in the
same folder: zh-CN and zh-TW both gave "zh", and the last one downloaded
overwrote the other. Mapping the Chinese locales apart would have fixed
only that pair, and each new variant (pt-BR, es-MX, en-GB...) would have
needed its own entry.

Use %locale% instead, which is unique per locale, like the backend already
does with %locale_with_underscore%. format-deploy keeps the keys of
translations.json as they are today (fr, de...) and only keeps the region
(zh-cn, zh-tw) when several locales of the same language are translated.
The language detection and the language picker already match a locale
exactly before falling back to its language, so they need no change.

The export pattern is stored by Crowdin with the source file: the sources
must be uploaded again before the next download, and the zh-CN data that
was polluted with Traditional Chinese still needs to be re-uploaded.

Fixes #2749
2026-10-02 12:12:52 +02:00
Ovgodd 8a7416cb3e 💄(frontend) redesign 401 error standalone page
Reuse the 404 standalone layout and keep the existing sign-in flow.
2026-10-02 11:42:23 +02:00
Ovgodd 2a4131a188 🐛(frontend) keep caption and alignment when replacing an image
The antivirus loader rebuilt the image block with an empty caption and
left alignment, so both were lost after a replace.

Signed-off-by: Ovgodd <c.gromoff@gmail.com>
2026-10-02 11:02:29 +02:00
Gene WoodandAnthony LC 6a3fd49348 📝(docs) fix markdown typo in installation README
Signed-off-by: Gene Wood <gene_wood@cementhorizon.com>
2026-10-02 10:25:35 +02:00
Manuel RaynaudandAnthony LC 9de17b3f82 👷(ci) cancel the superseded runs of a pull request
adding a concurrency group will now stop running CI if a new push is
made on the same branch.
2026-10-01 16:29:43 +02:00
Manuel RaynaudandAnthony LC 670cbe4beb 👷(ci) check the load-test tooling: swarm, canary, k6 and dashboards
Add missing jobs to run the tests in swarm and canary projects. For k6
the project is inspected using k6 docker image and for the grafana
dashboard we check if the generated dashboard equals the one existing.
2026-10-01 16:29:43 +02:00
Manuel RaynaudandAnthony LC d11d911c77 👷(ci) lint, typecheck, build and test the collaboration server
The CI was not setup for yhub, we added the missing jobs: lint, test and
typecheck
2026-10-01 16:29:42 +02:00
Anthony LC a2e7f4118c ⬆️(Dockerfile) bump Playwright base image to v1.63.0
The Playwright base image in the Dockerfile has been
updated from v1.62.1 to v1.63.0, in order to follow the
version of the `playwright` package in package.json.
2026-10-01 11:25:19 +02:00
Anthony LC 978a8205ea 🔒️(js) fix security warning
High:
- @xmldom/xmldom (13 advisories, CVE-2026-83612…)
- fast-uri GHSA-f65p-4m7j-42xc and 5 more
- js-yaml CVE-2026-84375
- browserslist CVE-2026-73089, CVE-2026-73088
- svgo CVE-2026-84370
- ws CVE-2026-48779

Moderate:
- vitest CVE-2026-84373
- qs CVE-2026-82562, CVE-2026-82417
- fflate CVE-2026-45820
- baseline-browser-mapping CVE-2026-45819
- colord GHSA-2wm5-q62r-hmrv

Low:
- @ai-sdk/provider-utils CVE-2026-8769
- postcss-selector-parser CVE-2026-9358
2026-10-01 11:25:18 +02:00
Anthony LC 3be8a858f2 ⬇️(frontend) downgrade blocknote dependencies to 0.54.0
The export feature is not working correctly with
the newer version, so we are downgrading to 0.54.0.
We need to refactorize the code to be compatible
with typst in order to work correctly.
2026-10-01 11:06:33 +02:00
Anthony LC 30dc14f78d ✅(dependency) add vite dependency
Recent upgrade of Vitest dependency needed
Vite dependency in order to work with the latest
Vitest features.
2026-10-01 11:06:33 +02:00
Anthony LC 77ea3b2c8e 🚨(dependency) update ui-kit tokens
We bumped the UI-kit dependency, we need to update
the UI-kit tokens accordingly.
2026-10-01 11:06:33 +02:00