Commit Graph
2373 Commits
Author SHA1 Message Date
Kevin JahnsandAnthony LC a968dbe2b3 🔒️(collaboration) reject admin jwts not issued for the yhub audience
yhub verified Django's RS256 admin JWT without checking "aud", so the
y-converter token Django hands to the converter process was replayable
here — and admin: true short-circuits getAccessType to "rw" on every
document, plus the backend-internal reset-connections purpose and the
X-User-Id attribution override. Require aud: "yhub", as y-provider
already does for its own audience. Nothing in the backend calls yhub's
admin endpoints yet, so no caller is affected.

Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:38:16 +02:00
Kevin JahnsandAnthony LC 0714835a12 (collaboration) soft-migrate legacy S3 documents into yhub
With SOFT_MIGRATION=true, the first access to a document yhub does not
know yet fetches the legacy snapshot from Django's S3 media bucket
({id}/file, UTF-8 base64 of a raw Yjs update), seeds the room through
the compute pool - attributed to "system" with a migration=s3 custom
attribution - and only then admits the connection, so the initial sync
always includes the seed. Now that the frontend no longer bootstraps
rooms client-side (content GET/PATCH removal), this is the only path
that brings legacy content into yhub; keep the flag on until a batch
backfill has migrated the full corpus.

A missing S3 object is the brand-new-document case and yields an empty
room; every real failure fails closed (opaque 401, y-websocket retries
with backoff). Existence is probed postgres-first (bare SELECT, then
the valkey stream, then the SELECT again to close the compaction
race). Guard rails: a per-docid verdict cache (poison objects cannot
sustain an S3 retry storm, transient errors expire in 15s, per-replica
seed backpressure denies once without caching), in-flight dedup, a
token-owned cross-replica valkey lock released by compare-and-delete,
a 10s S3 fetch timeout that also destroys a late-arriving response
stream, and the same 10MiB decoded cap as create-ydoc. Concurrent
seeds stay correct regardless: the frozen snapshots share one Yjs
lineage, so duplicates merge as CRDT no-ops.

Also reject non-lowercase docids (Django serializes UUIDs lowercase; a
case variant would open a parallel room and miss its S3 object) and
refuse to boot when AWS_S3_ENDPOINT_URL carries a path the minio
client cannot address. On AWS the read-only credentials must include
s3:ListBucket so a missing object surfaces as NoSuchKey rather than
AccessDenied - see the README for the full guarantees and ops notes.

Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:36:39 +02:00
Kevin JahnsandAnthony LC 560fc15062 🙈(dev) ignore playwright-mcp browser artifacts
The Playwright MCP browser session writes snapshots and console logs
into .playwright-mcp/ at the repository root while driving the app
during development — keep them out of version control.

Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:35:59 +02:00
Anthony LC efe4d44316 🔥(frontend) remove content GET PATCH
We remove the code related to the content GET and
PATCH endpoints, as they are no longer used in the
codebase. The yhub server will handle the content
management directly, providing the content and
managing the updates. This change simplifies the code
and reduces the complexity of the frontend application.

We will need to reimplement the saving mechanism
in the service worker when we are offline.
Let's wait that the service is fully developed
on the yhub side before we implement this feature.
2026-09-04 15:35:59 +02:00
Anthony LC 7e671ffd44 🔥(project) remove occurences of COLLABORATION_SERVER_SECRET
COLLABORATION_SERVER_SECRET is no longer used in the codebase,
so we can remove it from the codebase.
2026-09-04 15:35:27 +02:00
Anthony LC 8b7d2079d8 🔥(frontend) remove "can-edit" mechanism
We will not block anymore the users not connected
to the collaboration server from editing the document,
we will have an HTTP fallback instead, so we can
remove the "can-edit" mechanism and the related code.
2026-09-04 15:35:27 +02:00
Kevin JahnsandAnthony LC 36b254f3e7 🔒️(collaboration) harden the create-ydoc endpoint
Address the findings of an adversarial review of the new endpoint:

- Only the backend admin token may attribute content to another user via
  the X-User-Id header. The endpoint uses the default access purpose, so
  any editor with update ability can call it — honoring the header for
  them would let an editor forge the attribution history of the first
  revision (the websocket path likewise stamps the server-side
  identity). Regular callers now always author as themselves; verified:
  an editor session posting X-User-Id gets its own userid stamped.

- Reject non-main ?branch= requests (400). Cookie users are main-only
  via getAccessType, but the admin token bypasses it and could seed an
  orphan (org, docid, branch) room no user-facing path reads — while
  dodging the branch-scoped 409 existence check.

- Correct the concurrent-create comment: two racing creates merge as
  independently generated updates (fresh clientIDs), so the seeded
  content appears twice — user-visible duplication, not merely a
  doubly-attributed revision. Still accepted (Django creates each doc
  once and a duplicated seed is user-fixable), but the tradeoff is now
  stated accurately.

Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:35:26 +02:00
Kevin JahnsandAnthony LC 8469a68fd4 (collaboration) add create-ydoc endpoint on yhub
Python cannot call yhub's built-in PATCH ydoc api because its body must
be lib0-any encoded - a lib0-specific binary framing with no
implementation outside javascript. The new endpoint
POST /collaboration/create-ydoc/v1/{org}/{docid} accepts the raw binary
Yjs update (pycrdt get_update() / Y.encodeStateAsUpdate output) as
application/octet-stream, so Django can seed a document's initial state
with a plain requests.post(url, data=raw_bytes) - needed by the
server-side creation flows (file import, create-for-owner, duplication,
template instantiation) whose yhub rooms currently stay empty until the
first browser connects.

Strict create semantics: 409 when the room already has content
(checked via getDoc, covering persisted state and uncompacted stream
messages; yhub has no atomic create, concurrent creates merge via CRDT
and never corrupt). The initial content is attributed to the optional
X-User-Id header, else to the caller's identity. Access uses the
default purpose, i.e. standard document write access like the built-in
ydoc routes: the admin JWT, or a user session with update ability.
Malformed updates map to 400 (the compute worker rejects them and the
pool replaces the thread), empty updates to 400, bodies over 10MiB to
413.

Gotcha worth noting: req.bytes() resolves to a Node Buffer, but yhub's
compute-task schema validates with lib0's exact-constructor Uint8Array
check, so the body is re-viewed as a plain Uint8Array before it is
handed to the compute pool.

Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:35:25 +02:00
Anthony LC 8576654dad 🛂(backend) add audience to jwt
Add audience to the jwt, scoping the token to it
prevents an admin JWT issued for another backend
service from being replayed against y-provider.
2026-09-04 15:34:07 +02:00
Anthony LC 1c3f43b0dd 🔥(helm) remove occurences of Y_PROVIDER_API_KEY
Y_PROVIDER_API_KEY is no longer used in the codebase,
so we can remove it from the helm chart and the documentation.
We adapt the documentation to use the new JWT conversion
mechanism instead.
2026-09-04 15:34:06 +02:00
Anthony LC 4afd73124c 🛂(y-provider) verify jwt token instead of the shared api key
The /api/convert route no longer accepts the Y_PROVIDER_API_KEY shared
secret. It now verifies the admin JWT signed by Django against the
JWKS published on its /api/v1.0/jwks endpoint.
2026-09-04 15:34:06 +02:00
Anthony LC 7de483e001 🛂(django) use jwt token for converter services
The Y_PROVIDER_API_KEY shared secret is replaced by a
signed admin JWT when Django calls the y-provider
conversion endpoint.
2026-09-04 15:34:06 +02:00
Kevin JahnsandAnthony LC 0189ff3dcb (collaboration) add admin reset-connections endpoint on yhub 0.4.0
Add POST /collaboration/reset-connections/v1/{org}/{docid} (optional
X-User-Id header) to yhub-server. It distributes yhub recheckAuth: every
server re-runs the access check per matching connection and closes only
those whose access actually changed (close code 4401), so unaffected
clients see no reconnect churn. The endpoint authenticates with the
RS256 admin JWT issued by JWTService, verified against the backend JWKS
(new jose dependency); the admin token acts as the "system" user and is
the only principal granted the reset-connections access purpose. The
backend does not trigger it on permission changes yet - that wiring
comes separately, now that CollaborationService is gone.

yhub is upgraded to 0.4.0 and serves every route under the
/collaboration/ prefix (server.apiPrefix): the websocket moves to
/collaboration/ws/v1/docs, and the built-in document apis are meant to
be publicly exposed alongside it, with reset-connections as the one
backend-internal exception.

Also harden websocket auth: fail closed when the backend errors (only a
genuine 401/403 falls back to the anonymous identity, so a signed-in
editor can never hide from a targeted recheck under an anon userid) and
tolerate small clock skew when verifying the cached admin token.

Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:34:05 +02:00
Manuel RaynaudandAnthony LC cdffb78ee6 🔥(backend) remove CollaborationService and can-edit endpoint
The CollaborationService was doing nothing since we started the
migration to yhub, all the code using it is now removed. Also the
`can-edit` endpoint and all the safeguard mechanism relying on the
presence of other users connected to the websocket will not be used
anymore, it will be possible to replace all of this with yhub, so all
this code is also removed.
2026-09-04 15:32:06 +02:00
Manuel RaynaudandAnthony LC 48e039fadc 🔥(ci) remove checking print statement in lint-git
Since we use ruff, it is not needed anymore to check the presence of
print statement, the rule T201 is already doing it in a more performant
way.
2026-09-04 15:31:01 +02:00
Manuel RaynaudandAnthony LC 28baaf55bc 🔧(dev) generate the JWT signing key when bootstrapping the dev stack
Thw private key needed to generate a jwt token will be mandatory. In
order to ease the development we want to automate its generation
2026-09-04 15:31:00 +02:00
Manuel RaynaudandAnthony LC 261b730ff0 (backend) add a method to create a dedicated admin token
For now the only token we will need is ont with the admin claim set to
True. To not repeat the creation of this token again and again, we
created a dedicated method to issue this token in the JWTService class.
2026-09-04 15:30:15 +02:00
Manuel RaynaudandAnthony LC e5ef9c39a8 (backend) publish the JWT public key on a JWKS endpoint
The yhub service will need our public key in order to validate the jwt
token we will used. We choose to expose a jwks endpoint as it is a
standard wat to do this.
2026-09-04 15:30:15 +02:00
Manuel RaynaudandAnthony LC bd0b119dd7 (backend) add a service generating cached RS256 JWT tokens
We want to generate jwt token using the RS256 algotrithm. This token
will be used for internal call with the yhub service.
2026-09-04 15:28:51 +02:00
Kevin JahnsandAnthony LC ad89bb6fca 📄(collaboration) add license notice for yhub-server directory
Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:27:26 +02:00
Kevin JahnsandAnthony LC 77c8b2f138 ♻️(collaboration) switch collaboration server from hocuspocus to yhub
Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-04 15:27:25 +02:00
Nathan Panchout 1debd291d8 (frontend) cover presenter PDF export
Exercise PDF print pages, file blocks, and tall slide clipping.

Keep coverage scoped to the presenter export workflow.
2026-09-04 11:35:24 +02:00
Nathan Panchout b67039dc6c (frontend) export the presentation as a PDF
Render the slides off-screen as A4 landscape pages and print them via
the browser, one slide per page with the watermark. Add a "Download PDF"
action to the floating bar.

Closes #2446
2026-09-04 11:35:24 +02:00
Nathan Panchout eb6a0db31b (frontend) add a docs logo watermark to presentations
Show a monochrome Docs logo in the slide corner (web and PDF) to signal
the document was made with Docs.

Closes #2472
2026-09-04 11:35:24 +02:00
Nathan Panchout f3dc31c13d ♻️(frontend) let print DOM helpers target a custom root
wrapMediaWithLink / wrapInterlinksWithAnchor defaulted to document.
Accept a root node so the presenter export can reuse them on its
off-screen tree.
2026-09-04 11:35:24 +02:00
Manuel Raynaud 3bede0d9a0 🐛(backend) allow to configure settings DATA_UPLOAD_MAX_MEMORY_SIZE
Release 3.17.2 of DRF now takes care of DATA_UPLOAD_MAX_MEMORY_SIZE
and is checked when the body request is parsed. Before that, DRF wasn't
using it at all and we were only looking for custom settings linked to
the media and conversion file upload. We must now also configure this
setting.
2026-09-04 10:21:43 +02:00
MoraandAnthony LC b32b3f08c2 🐛(frontend) hide leave option when unauthenticated
Leave was shown in the document menu for anonymous visitors.
Hide it unless the current user can actually leave the doc.

Signed-off-by: Mora <razvi.morariu.mihai@gmail.com>
2026-09-04 09:04:12 +02:00
Anthony LC 059f1d004a 🔖(release) minor 5.6.0
Added:
- (frontend) Add "Copy link to block" feature
- (frontend) add word count to doc header toolbox
- (frontend) add find and replace feature to the editor

Changed:
- ️(frontend) use anchor links for interlinking sub-documents
- (frontend) reset side panel state between documents
- ️(frontend) announce search loading state for screen readers
- ♻️(frontend) change favorite to star
- 🚚(frontend) add doc move to doc options
- ♻️(frontend) unified menu
- (frontend) hide decorative emojis in document titles from SR
- ♻️(frontend) save the doc with a keepalive request when
  leaving the page

Fixed:
- 🐛(frontend) fix clipped formatting toolbar in new comment
  composer
- 🐛(backend) fix duplicating a document that has no content
- 📄(frontend) allowed partially export when MIT
- 🐛(backend) manage async support for Docs custom middleware

Removed:
- 🔥(backend) remove whitenoise package
v5.6.0-preprod v5.6.0
2026-09-03 17:00:36 +02:00
Anthony LC 75fe97272f 🐛(frontend) codeblock throws errors when unsupported language
Implementation of code block throws a errors when a `language` prop
is not supported by BlockNote, which crashes the whole editor.
In our case, the language is supported but flagged as an alias
(e.g. `js` instead of `javascript`),
but Blocknote does not resolve aliases and throws an error.

This file wraps the code block spec to normalize the `language`
prop before BlockNote renders it,
so a single legacy code block does not crash the whole editor.
2026-09-03 17:00:36 +02:00
AntoLCandAnthony LC 8b9fce27fd 🌐(i18n) update translated strings
Update translated files with new translations
2026-09-03 17:00:35 +02:00
Anthony LC ceae767825 ♻️(frontend) adapt side menu with Blocknote 0.54.0
Blocknote changed the way the side menu is positioned.
Because we are modifying the height of the heading blocks
we need to adjust the offset of the side menu to match
the new height of the heading blocks.
To do so we have to override the default offset of
the side menu with a custom offset that takes into
account the new height of the heading blocks.
2026-09-03 13:30:49 +02:00
Anthony LC 7c106e33a8 🔒️(js) fix security warning
- browserslist
- nanoid
2026-09-03 13:30:48 +02:00
Anthony LC ee624e875e (frontend) add math and diagram blocks to the editor
Last version of Blocknote added support for math
and diagram blocks. This commit updates the
dependencies to include the necessary toolbar
items to create math and diagram blocks in
the editor.
2026-09-03 13:30:47 +02:00
Anthony LC ac580bc81b 🚨(frontend) adapt codebase to Blocknote 0.54.0
We bumped Blocknote to 0.54.0, which introduced
some breaking changes. This commit adapts our
codebase to the new API and ensures compatibility
with the latest version of Blocknote.
2026-09-03 11:07:27 +02:00
renovate[bot]andAnthony LC 0e8bc3a518 ⬆️(dependencies) update js dependencies 2026-09-03 09:15:26 +02:00
Manuel Raynaud b12b3b5dd6 🐛(frontend) save the doc with a keepalive request when leaving the page
Since we fix the error in the backend application coming from middleware
not managing async request lifecycle, the e2e tests were failing. This
is because the browser cancel the request when made in the beforeunload
event. To fix it, we set the keepalive property on the fetch method to
True to not abort the request when the page is unloaded.
2026-09-03 08:25:15 +02:00
Manuel Raynaud c69e790703 🔥(backend) remove whitenoise package
whitenoise middleware is failing a lot with a cancelled exception from
asyncio. Using whitenoise is not needed in our case, we are just serving
an API with django and DRF. We decided to completely remove it.
2026-09-03 08:25:15 +02:00
Manuel Raynaud 53bf783447 🐛(backend) manage async support for Docs custom middleware
Docs have 2 custom middlewares, both are only managing sync
requests. With Python 3.13 we didn't have any errors, but
since we upgraded to Python 3.14, we have a CancelledError
exception. We decided to use the MiddlewareMixin from Django
that is sync and async capable and will be responsible for
executing both middleware in the good mode.
2026-09-03 08:25:03 +02:00
Anthony LC 5b24290923 ️(frontend) bind react-query cache tree invalidation with reloadTree
We need to invalidate the react-query cache during
some actions that modify the tree, invalidating
the react-query cache was not enough, we also needed
to reset the tree state to null, so that the tree with
react-arborist would be reloaded with the new data.

We now binded the reloadTree function with the
react-query cache invalidation, so that when we
invalidate the cache, automatically the tree state
is reset to null and the tree is reloaded with the
new data. No need to call reloadTree manually anymore.
2026-09-02 18:02:21 +02:00
Anthony LC 04446c3466 ️(frontend) improve accessibility of doc tree
We had different and duplicated accessibility behaviors
between the tree root and the subpages.
This PR unifies the behaviors and improves
accessibility of the doc tree.
We can now:
- directly focus on the tree then navigate
through the tree with the arrows keyboard
- When using F2, we focus on the actions, click
on escape to go back to the tree and continue navigating
- When using F2, and we arrives at the last action,
another F2 will go back to the start of the actions
- When using F2, you can then use the arrows to navigate
through the actions
2026-09-02 18:02:20 +02:00
Anthony LC e91f9dfc91 ♻️(frontend) unify toolbox menu
Depend the part of the app, we had different implementations
of the toolbox menu. This commit unifies the implementation
and uses the same component for both the docs grid, the doc tree
and the doc header.
It will make it easier to maintain and add new
features to the toolbox menu.
2026-09-02 18:02:18 +02:00
Cyril 3d3f58582d ️(frontend) hide decorative emojis in document title from SR
Mark UI title emojis as aria-hidden while keeping accessible text labels.
2026-09-02 11:12:55 +02:00
Amine BOUKERFAandGitHub 681f9a8c40 🐛(backend) fix duplicating a document that has no conten
Document.content reads from object storage and returns None when nothing
was ever written there. That None, raised "content should be a string.",
so the duplicate endpoint answered a 500. Default to an empty string instead.
    
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
2026-09-02 06:46:37 +00:00
renovate[bot]andGitHub 4633cc7690 ⬆️(dependencies) update djangorestframework to v3.17.2 [SECURITY] 2026-09-02 02:42:52 +00:00
Anthony LC 3646d7dc42 🚚(frontend) add doc move to doc options
In order to harmonize the doc options, we are adding
the "Move to my docs" and "Move into a doc" options
to the doc options menu.
This allows users to move documents directly from
the options menu, providing a more streamlined
experience.
2026-08-28 12:56:49 +02:00
Anthony LC 85177dc3ca (frontend) add find and replace feature to the editor
We have added a new feature to the editor that allows
users to find and replace text within their documents.
This feature enhances the editing experience by
providing a convenient way to search for specific
words or phrases and replace them with new content.
2026-08-28 12:20:06 +02:00
Anthony LC 1c52633f96 ️(frontend) responsive throttling instead of debouncing
We changed the responsive store to use throttling
instead of debouncing for the window resize event.
This change ensures that the store updates more
consistently during rapid window resizing,
providing a smoother user experience.
2026-08-28 11:09:13 +02:00
Anthony LC 672f84d8bc (frontend) add word count to doc header toolbox
We can now see the word count in the doc header toolbox.
2026-08-28 11:09:12 +02:00
Anthony LC e0fcd2a01e ⬆️(frontend) replace ui-kit by ui-components
A major release was made on the ui-kit side,
ui-kit is replaced by ui-components, which is a new
package that contains the same components as ui-kit
but with some improvements.
To build the tokens, a new dev dependency is necessary,
which is @gouvfr-lasuite/ui-tokens.
These replacements bring better separation of concerns
and a better architecture for the future.
We need to adapt our codebase to use the new package
and the new dev dependency.
2026-08-28 11:09:11 +02:00
Anthony LC 944ea131c4 📄(frontend) allowed partially export when MIT
When MIT, we were not able to export anything.
But actually, the export HTML and Print are allowed
for MIT, so we should allow them.
We now allow partially export when MIT, but the
AGPL export is still not allowed (pdf / odt / docx).
2026-08-26 13:17:27 +02:00