Users mentioning themselves got an email about something they just
did, which is noise. Keep recording the mention, skip the email.
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
The cache key claimed before sending outlived a failed notification,
so the next mention in the same context was silently dropped until the
key expired although nobody had been notified. Delete it unless the
email went out and `notified_at` was saved.
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
Gate the ability to mention to users with explicit
access so only collaborators can mention collaborators.
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
A reader cannot see comments, so a mention would lead them nowhere.
Reject readers as mention targets. while users allowed to comment
see each other so they can pick who to mention.
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
Use cache.add as a distributed lock keyed by document/user/thread to
ensure only the first concurrent mention sends the notification
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
Add a dedicated "mention" throttle scope limiting calls
to the document mention endpoint to 30 requests per minute.
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
Move the mention email off the request/response cycle into a Celery task
so the SMTP round-trip no longer delays the API response.
Signed-off-by: BOUKERFA Mohamed El Amine <boukerfa.ma@gmail.com>
Allow users with at least the commenter role on a document to mention
other collaborators in the document body or in a comment thread via
POST /documents/{id}/mention/. The mentioned user must already have
access to the document.
The mention record is always created, and the mentioned user is
notified by email with a link to the anchor, unless a notification
was already sent in the same context (document body or specific
thread) within a configurable cooldown period (15 minutes by default).
Signed-off-by: Mohamed El Amine BOUKERFA <boukerfa.ma@gmail.com>
Return all accesses on the document and its ancestors to any
user with access, keeping the limited user details serializer
and adding the user id to it, so that collaborators allowed
to comment can identify and mention each other.
Signed-off-by: Mohamed El Amine BOUKERFA <boukerfa.ma@gmail.com>
We can now paste docs url that will become interlinking.
So we don't want to pass titles through the inline content itself
anymore, to not leak any titles. The problem is that inline content
Inline content mappers are not async, so titles must be
resolved upfront.
We adapted the export interlinking to get titles from
a pre-resolved map instead of relying on inline content.
Pasting a bare link to another doc on the same domain now inserts an
interlink instead of a plain hyperlink, so it benefits from the same
title-sync and navigation behaviour as links added through the `/` or
`@` menu. Pasting over a selection, inside a code block, or a link the
current user can't resolve will display the doc id instead.
We want to introduce a feature flag that allows
duplicating documents along with their children when
enabled. We will be able to control not only
frontend side but also backend behavior
regarding document with children duplication.
We want to have a fine grained control over the
Duplicate with Children feature.
By adding the feature flag for Duplicate with Children,
we can enable or disable this feature for specific
users or groups without deploying new code.
This allows us to test the feature in a controlled
environment and gather feedback before a full rollout.
The quay.io/minio/minio and quay.io/minio/mc images now answer 401, and
the minio images are gone from Docker Hub as well: the dev stack, the
backend CI job and the helm dev-backend could not pull them anymore.
[pgsty/silo](https://hub.docker.com/r/pgsty/silo) and
[pgsty/mc](https://hub.docker.com/r/pgsty/mc) are community builds and drop
in replacement of minio. Only the executable name is changing from minio
to silo.
Search results were cmdk items navigating with router.push on select,
so the browser had no link to open elsewhere. Each result is now a real
link to the document: a plain click still goes through the quick search
onSelect, while ctrl, cmd, shift or middle click let the browser open it
in a new tab or window and keep the search modal open.
Signed-off-by: Armand <schneideretcollier.innovation@gmail.com>
After implementing the websocket and api stress tests, this last step is
using playwright to simulate a real user using docs. A real chromium
will be used for this. The idea is to see what a user feels when Docs is
under load.
When metrics is enabled, we want to create a ServiceMonitor od
PodMonitor in order to scrap the metrics from django and yhub. We have
to add an exception on the redirect to ssl for the metrics endpoint,
like for the probes endpoint, the traffic is internal.
To stress test the backend application we will use the k6 project from
grafana. 2 scenarios are implemented and documented in the README. How
to use this project is also documented in the README
standlone package simulating traffic on the yhub server. It reuses the
same stack used bu the frontend application to connect to the websocket.
How to use it is explained in its README file.
We added custom metrics when the monitoring is enabled, we will measure
the cost of calling outside services like yhub and the converters, the
database pool and celery queue.
We don't want to manage a valkey operator and cluster inside the Docs
repo, this will be managed outside. For dev purpose we only need a
standalone instance of valkey.
To test the application on real condition we want to authenticate users.
The authentication is made using OIDC and we don't want to depend on
this during the tests. To bypass it, we will create real user sessions,
export thei identifiers and then use it later with the tool firing the
requests. The session are linked to real users, everything is made in a
dedicated django application and this application is explicitely loaded
in a dedicated environment and can't be used in the Production
environment.
Same we did before with django-prometheus, we want metrics for yhub, the
is no integration available like django-prometheus so everything is
craft in the repository. The documentation is also available in
documentation/metrics.md
We want to export metrics to prometheus. We install for this
django-prometheus. By default the feature is disabled and must be
explicitly enabled. A complete documentation is available in
documentation/metrics.md
We need to use valkey instead of an old redis version. We decided to use
the operator https://github.com/chideat/valkey-operator/ and configure
it in both feature and dev environments.
we have to import @y/y dependency, we use it in the migration part of
the application and @y/hub is also using it. This can lead to a
situation where 2 versions of @y/y are installed, this is not something
we want.
The offline support couldn't work with the existing
implementation anymore, because there is no request
to get or save data anymore, everything is handled
with web sockets.
In order to support offline functionality, we
leveraged y-indexeddb to store and synchronize
local changes, ensuring that the application remains
functional even when offline.
Version grouping used a hard-coded 60s window, and its
timestamps are minted server-side.
We now expose COLLABORATION_VERSION_GRANULARITY_MS
through /config, read it in useDocActivity.
By doing so, we can control the granularity of
version history with settings, it can be adjusted as
needed. It will help us to test different version
history granularities in our e2e tests.
Divide server.ts into 3 focused modules:
- config.ts: every environment variable the server reads, parsed and
validated at import time,
- backend.ts: the calls made to the Docs Django backend, the token
signing and the JWKS both ends verify each other with,
- api.ts: the custom REST endpoints mounted under /collaboration/.
server.ts keeps only what wires these together: the auth plugin and its
legacy-store seed (which closes over the yhub instance), the persistence
plugins, the worker events and the createYHub call.
The AppAuthInfo identity shape moves to yhub.ts so server.ts and api.ts
can share it without importing from each other.
Tests follow the split: config.spec.ts and backend.spec.ts are new,
server.spec.ts narrows to the composition root's boot contract. No
behaviour change.
Convert the yhub-server source and tests from plain JS/mjs to
TypeScript: move sources under src/, add tsconfig, tsconfig.build,
eslint flat config and nodemon config, and update the Dockerfile,
compose and CI workflow for the build step.
Every other package in this repository is installed
with yarn; yhub-server was the only one on npm,
with its own package-lock.json.
To ensure consistency it now uses yarn like the rest
of the project.
package-lock.json is replaced by yarn.lock, a packageManager
field is added, and the Dockerfile, CI workflow, Makefile,
helm init-db job and the documentation move from
`npm ci` / `npm run init-db` to
`yarn install --frozen-lockfile` / `yarn init-db`.
We use vitest in most of our applications,
to ensure consistency and maintainability across our
codebase we are now using vitest for yhub-server as well.
We added more tests on yhub-server.
The tests cover migration, permissions, and server
functionality. We also added a helper file for test
utilities.
The content is not managed anymore in the Document object nor the django
application. The last piece using it was the versioning API and it has
been deleted previously. There is no more reason to keep code related of
the content in the backend application. The only remaining code is the
`file_key` property in the model. It is kept as a safe guard use by the
clean_document management command. If the file is kept on the bucket,
then the document will be reseed with it the first time the document
will be reopen.
Previously we added to retrieve document response a new property
user_access_since that contains the date from when the user started to
have access to the document. The way t was made added an other
annotation to the Document queryset making the sql query more and more
complex. We decided to lighten the queryset and expose the access the
user has on the document instead and read the history from the
created_at property.