Commit Graph
61 Commits
Author SHA1 Message Date
638a2de121 ♻️(frontend) drop next.js for vite + tanstack router (#675)
Next.js has been concerned by several CVEs last time and it's clearly
overkill for our needs (static export) so in order to enlight our stack
we migrate to vite & tanstack-router

Co-authored-by: jbpenrath <jb.penrath@gmail.com>
2026-06-09 11:58:51 +02:00
Sylvain ZimmerandGitHub e3c183a625 🐛(smtp) fix opportunistic TLS against MXes with mismatched certs (#687)
The "may" level was verifying the peer cert and falling back to
cleartext on mismatch, which then bounced on STARTTLS-required
servers (e.g. Mandrill's SES-backed inbound returns 530 to MAIL
FROM in cleartext). Realigned on Postfix's documented behavior:

- "may":    opportunistic TLS, no cert verification.
- "secure": mandatory TLS + CA chain + hostname check; defers if
            STARTTLS isn't advertised or handshake fails.
- "encrypt" is dropped (replaced by "secure").

Also wires MTA_OUT_SMTP_TLS_SECURITY_LEVEL through both the direct
and relay paths — it had been declared but never read — and
collapses the four proxy_* kwargs + sender_hostname of
send_smtp_mail into a single SmtpProxy dataclass.
2026-06-04 16:20:57 +02:00
Sylvain ZimmerandGitHub f7786b3d0f (backend) report selfcheck status to Sentry crons (#694)
Use the new (documented) env var MESSAGES_SELFCHECK_SENTRY_MONITOR_SLUG
to enable
2026-06-04 00:38:59 +02:00
Sylvain Zimmerandjbpenrath f360570798 (blobs) implement tiered storage and refactor blobs/attachments
This allows to use S3-compatible object storage to offload blobs, making Postgres much lighter. We design for storing ~1B emails on a single instance.

We also take this opportunity to do model changes on blobs & attachments.

Migration 0027 is one-way, no going back after this one so check your backups.
2026-05-20 20:48:00 +02:00
Jean-Baptiste PENRATH 21255a4c4a (global) allow thread assignation (#645)
As a follow-up of the mention feature, we build upon ThreadEvent & UserEvent
models a feature to assign users to a thread.
We allow to filter mailboxe's inbox through assignation state (assigned to me, unassigned).
The thread share modal has been forked from ui-kit to be able to list users of each
mailbox and add a cta to assign them to the thread. A section above shows assigned users.
2026-05-19 14:44:26 +02:00
Jean-Baptiste PENRATHandGitHub 6be3b9e129 ♻️(frontend) refactor thread query cache management (#642)
The thread query is an infinite one and the frontend logic is
based on the structuralSharing concept of react-query to
optimiscally update the react query cache on thread mutation in
order to improve ux. This part is a tricky one and it's easy to
introduce regression, that's why refactor it by moving the corresponding
logic into a mailbox-cache module, use a better naming (pin instead of optimistic)
and battle test it.
2026-05-06 19:39:07 +02:00
Jean-Baptiste PENRATHandGitHub 22a8c35140 🔧(frontend) allow to use specific channel id for home feedback widget (#655)
Add `NEXT_PUBLIC_FEEDBACK_WIDGET_HOME_CHANNEL` env var to be able to
set a specific channel id to receive feedback from unauthenticated
users.
For configuration ease and backward compatibility, if
`NEXT_PUBLIC_FEEDBACK_WIDGET_HOME_CHANNEL` is not set, the home feedback
widget fallback to `NEXT_PUBLIC_FEEDBACK_WIDGET_CHANNEL`.
2026-05-05 14:23:42 +02:00
jbpenrath a29b25a7c0 ️(backend) replace delete_by_query with bulk delete by _id
OpenSearch was returning 503/429 on delete_by_query under the load of
the periodic reindex. Each bulk_reindex_threads_task could fire up to
100 delete_by_query calls (one per chunk of 100 threads) to sweep
orphan messages, and bulk_delete_threads_task did one more to drop a
thread and all its children. delete_by_query holds a scroll context,
scans the index and refreshes per call — far heavier than the bulk
upserts running alongside it.

Tracking message deletes explicitly at signal time lets us replace
every hot-path delete_by_query with a bulk delete by _id:

- New search:pending_delete_messages set storing thread_id:message_id
  pairs, fed by Message.post_delete (covers cascaded deletes too).
- New bulk_delete_messages_task issuing bulk DELETE actions with the
  parent thread_id as routing.
- bulk_delete_threads_task rewritten to bulk DELETE thread parent docs
  by _id; child message docs ride the new task via cascaded signals.
- _purge_orphan_docs and the per-chunk purge in reindex_bulk_threads
  removed; reindex is now pure upsert.
2026-04-29 10:19:33 +02:00
Jean-Baptiste PENRATHandGitHub 5054daa879 🌐(frontend) force default language (#647)
Currently, when user has not its language set into local storage, we
retrieve
the default language through the navigator language. In some instance,
we would
like to enforce the default language. So we add a new env var
`NEXT_PUBLIC_FORCED_DEFAULT_LANGUAGE`, if this one is set to `true`, we
skip
the navigator.language and use the `NEXT_PUBLIC_DEFAULT_LANGUAGE` as
default
2026-04-28 19:38:14 +02:00
jbpenrath 81da914ca4 ️(backend) improve search_reindex bulk payload
`bulk_data` method was missing some options (timeout, max_retries) so
sometimes when
the payload was heavy, the request can failed because the timeout was
too slow by default
(10s) and there is no `max_retries`. Furthermore, we build bulk payload
by chunking thread
but it did not check the payload bytes size, so in case of heavy
message, the payload could
be heavy. Now a max_bytes limit is set (50 Mib by default)
2026-04-27 18:26:03 +02:00
jbpenrath 6d0eb0d234 ️(backend) defer indexation tasks
Opensearch index is updated each time a Thread, Message or MessageRecipient
is updated through signals. The current logic has performance issues has
n update of a resource will generate n celery task to update the same
resource... So this work aims to batch updates. Index is updated each
30s and resource ids is deduplicated to only update a resource once.
Furthermore, in an import context, the index will be updated only
when the import will be completed to prevent to spam the celery
worker with numerous indexation task.
2026-04-27 18:26:03 +02:00
jbpenrath a1fdca19b2 Revert "(global) allow thread assignation"
This reverts commit 267372520d.
2026-04-27 18:14:09 +02:00
jbpenrath 682530b3b7 Revert "fixup! (global) allow thread assignation"
This reverts commit b04b07a465.
2026-04-27 18:14:01 +02:00
jbpenrath b04b07a465 fixup! (global) allow thread assignation 2026-04-23 20:21:20 +02:00
jbpenrath 267372520d (global) allow thread assignation
As a follow-up of the mention feature, we build upon ThreadEvent & UserEvent
models a feature to assign users to a thread.
We allow to filter mailboxe's inbox through assignation state (assigned to me, unassigned).
The thread share modal has been forked from ui-kit to be able to list users of each
mailbox and add a cta to assign them to the thread. A section above shows assigned users.
2026-04-22 20:21:31 +02:00
Jean-Baptiste PENRATHandGitHub 1044614a76 (global) add mention notifications via UserEvent (#621)
ThreadEvent IM mentions previously lived only inside the event payload,
with no per-user tracking, so a user had no way to see or filter the
threads where they were mentioned. The new UserEvent model materializes
mentions as first-class records (one row per mentioned user per event),
reconciled by a post_save signal whenever a ThreadEvent is created or
edited.

ThreadEvent edits and deletes are now bounded by THREAD_EVENT_EDIT_DELAY
(1h default) so UserEvent records cannot drift out of sync with stale
audit data past the window.
2026-04-10 00:54:39 +02:00
Jean-Baptiste PENRATHandGitHub 4a59033a98 🔧(backend) put split thread feature under a feature flag (#624)
Allow to dis/enable split thread feature through environment variable
`FEATURE_THREAD_SPLIT`
2026-04-09 19:19:12 +02:00
Niels Kersicandjbpenrath 1767e17117 (global) support silent login
Allow to enable silent login through envvar
`FRONTEND_SILENT_LOGIN_ENABLED`
2026-03-18 11:09:52 +01:00
e1970cc732 (autoreply) add autoreply feature (#569)
This reuses message templates and allows to define scheduling

Co-authored-by: Sylvain Zimmer <sylvinus@users.noreply.github.com>
2026-03-16 16:59:54 +01:00
Sylvain ZimmerandGitHub d153f5ad69 (entitlements) add DeployCenter backend for syncing maildomain admins (#572)
Entitlements system with pluggable backends and automatic mail-domain admin sync on login.
Provisioning API to create/update mail domains (bulk input, custom attributes)
Feature flags to enable/disable mail-domain creation and access management; UI and public config expose these flags
2026-03-03 15:06:55 +01:00
Jean-Baptiste PENRATHandGitHub 516d9dfa58 (frontend) add configurable help center button in header (#537)
Allow to configure a help center URL via the
 NEXT_PUBLIC_HELP_CENTER_URL environment variable. The button
 adapts based on what's configured:
- Help center only: opens external link directly
- Feedback widget only: opens widget directly
- Both: shows dropdown with both options

The button is now displayed in the header to save space in sidebar,
 rather than in the left panel.
2026-02-23 16:34:33 +01:00
Sylvain ZimmerandGitHub f18277b742 🔨(devx) update developer experience: uv, rustfs, caddy, new makefile (#556)
This large PR modernizes the backend of the app:
 - Python Dependency Management: Poetry → uv
 - Object Storage for local dev: MinIO (now unmaintained) → RustFS
 - Makefile Target Standardization to align with other LaSuite repos
 - Internationalization Removal on backend: we only care about i18n on the frontend
 - Backend dependencies upgrade
2026-02-23 12:30:36 +01:00
Sylvain ZimmerandGitHub ab24b60160 (throttle) add outbound message recipients throttling (#506)
This is inspired by #466 but focuses on outbound backend-only changes.
2026-02-20 00:41:58 +01:00
Stanislas BruhièreandGitHub 850929ca48 (mda) use a webhook+logging instead of a pushgateway for selfchecks (#550) 2026-02-19 20:40:14 +01:00
Sylvain ZimmerandGitHub e5ce447905 (exports) add task to export a mailbox in mbox, with labels (#553)
This also makes sure we can reimport X-Keywords later.

For now it's admin-side only, we'll add a user-facing UI. First part of #211.
2026-02-19 18:10:19 +01:00
Sylvain ZimmerandGitHub 9b5375cba3 (import) add support for PST imports & stream data for mbox (#544)
Fixes #183. Replaces #517.
2026-02-19 14:03:43 +01:00
jbpenrath c53e8bfcd4 (global) allow to add image block into template and signature composers
Images are embedded as base64 data URLs directly in the BlockNote content,
unlike the message composer which uses blob uploads + CID references.
This approach keeps templates and signatures self-contained without
requiring an attachment system.

Furthermore, a email-safe safe html exporter has been created to
serialize blocknote content into html.

A new backend setting MAX_TEMPLATE_IMAGE_SIZE (default 2 MiB) controls
the maximum allowed image size for these composers.
2026-02-17 19:02:28 +01:00
jbpenrath ffca2231e6 (global) add outbox conditional folder
Add outbox conditional folder to display messages having message recipients with None, retry or failed state.
Inform visually the user is something is wrong with a message.
Allow user to update MessageRecipient from failed to cancelled to hide ui warning.
2026-02-05 19:04:02 +01:00
Sylvain ZimmerandGitHub e6296f9b3a (integrations) add integrations view in mailbox (#488)
For now this allows users to create feedback widgets linked to their
mailbox. We will also have API keys and recurring imports there.
2026-01-25 17:29:19 +01:00
Sylvain ZimmerandGitHub 9a48eda9c6 ♻️(permissions) refactor some permissions code for viewsets (#503)
We want to avoid relying on role__gte, so we add role list aliases.
2026-01-24 13:48:43 +01:00
Sylvain ZimmerandGitHub 681a1481af (worker) add worker.py command and improve task routing on queues (#504)
This avoids a case where a very large import would overflow the
"celery" queue with reindex tasks that would block the inbound
processing tasks. Now we have good priorization.
2026-01-24 11:53:22 +01:00
Jean-Baptiste PENRATHandGitHub 606f0cc144 🔧(backend) update Django admin URL handling (#498)
Updated the DJANGO_ADMIN_URL setting to ensure it does not end with a '/'.
It allows to keep simple redirect logic from the nginx proxy server and avoid
issues with django admin route paths.
2026-01-22 10:01:37 +01:00
812cf51915 (all) allow to display external images through proxy (#469)
Allow to users to display external images from their email
through a secure proxy endpoint to ensure security and privacy
and respect iframe csp policy.

Co-authored-by: =?UTF-8?q?Ri=C3=ABl=20Notermans?= <riel@mosa.cloud>
2026-01-12 17:09:32 +01:00
jbpenrath c3a439deae 💄(frontend) migrate to ui-kit v2
Migrate frontend to ui-kit v2. Improve layouts on the road and fix
major a11y issues
2025-12-15 17:59:51 +01:00
Jean-Baptiste PENRATHandGitHub 5161978fcd 🔊(frontend) allow to capture exception through Sentry (#448)
Install and setup a generic error handler that send event to
Sentry if configured or just log error in console otherwise.
2025-12-09 17:51:00 +01:00
Niels Kersicandjbpenrath 1ac92fe44b 🔧(backend) add configurable Drive app name
Add DRIVE_APP_NAME variable that gets exposed via the config endpoint then
update frontend translation to use this variable into it.

Co-Authored-By: jbpenrath <jb.penrath@gmail.com>
2025-12-09 14:19:53 +01:00
jbpenrath 24f2644f0d (interop) allow to save an attachment into Drive workspace
Setup Drive resource server query to retrieve and save files from a configured
Drive instance through OIDC resource server.

https://github.com/suitenumerique/drive/pull/379
2025-12-01 19:32:07 +01:00
jbpenrath b380612a70 ♻️(authentication) use django-lasuite oidc backend
Insatll django-lasuite and our custom authentication backend to inherit
from the LaSuiteOIDCAuthenticationBackend
2025-12-01 16:54:33 +01:00
jbpenrath 369bf83e05 🔧(e2e) setup e2e environment
Create a new project e2e based on playwright and setup a full dockerized e2e
environment
2025-11-25 23:27:48 +01:00
Berry den HartogandJean-Baptiste PENRATH eebdff3e32 🌐(frontend) add dutch 2025-11-04 22:53:44 +01:00
Sylvain ZimmerandGitHub 54b14f9304 (features) add new feature flag env vars (#387)
Rename old AI_FEATURE env vars and add 2 new ones
2025-10-17 23:27:37 +02:00
Jean-Baptiste PENRATHandGitHub 10be269761 (import) Enhance message import through multipart upload (#372)
- Added multipart upload capabilities for EML and MBOX files to the
  message imports bucket.
- Introduced new API endpoints for initiating uploads, completing
  uploads, and aborting uploads.
- Updated serializers and viewsets to handle file uploads efficiently.
- Improved frontend components to display upload progress and handle
  file uploads seamlessly.
- Adjusted backend services to process uploaded files asynchronously,
  ensuring better performance and user experience.
- Updated documentation and tests to reflect the new functionality.
2025-10-14 17:31:17 +02:00
jbpenrath 032501eb5f 🔧(crowdin) setup a crowdin workflow
Enable commands to upload and download translation file to/from crowdin.
Setup ci workflow to automatically update translation files on each merge on
main branch. Also add a worklow that can be manually triggered which is in
charge to update application translation files then open a PR.
2025-10-06 10:06:14 +02:00
jbpenrath f2c407f6a0 🌐(frontend) manage translation keys through i18next-cli
Rework totally the way to manage frontend translation keys. Currently we were
managing manually all those keys but now we want to use crowdin to manage our
languages so first we need to be able to extract frontend translation keys in
dedicated files.

Furthermore, we allow to setup frontend application languages and default
language through environment variables
(NEXT_PUBLIC_DEFAULT_LANGUAGE and NEXT_PUBLIC_LANGUAGES)
2025-10-06 10:06:14 +02:00
Sylvain Zimmer bcab071224 🐛(mta-in) change MDA_API_BASE_URL default value 2025-09-29 10:57:55 +02:00
Sylvain Zimmer 227e46b976 📝(readme) update architecture image and include in README 2025-09-27 12:58:48 +02:00
Sylvain ZimmerandGitHub f4eac6dd8f (channels) add feedback widget and multiple inbound channels support (#301)
This PR adds a new build system for embeddable widgets and a first implementation of a "Feedback" popup widget.

It also refactors inbound message routes into channels, of which there are 2 for now: MTA (by default) and Widget. More to come!
2025-09-24 14:30:40 +02:00
Sylvain ZimmerandGitHub 423bf50bc9 (smtp) implement smtp_tls_security_level, follow RFC more closely (#336)
This fixes delivery errors seen in the wild where:
 * Some servers advertise STARTTLS but have broken certificates.
   Because of this, we follow Postfix's recommentations to use
   opportunistic TLS by default.
 * As we are sending emails straight to other SMTPs in direct mode,
   we need to have stricter RFC compliance for our raw mime DATA, namely
   CRLF endings and maximum line length. This was transparently done
   by Postfix in relay mode.
Some env vars have also been renamed for clarity.
2025-09-19 12:28:38 +02:00
d64679af5c (metrics) add prometheus metrics endpoint (#329)
* (backend) add base django-prometheus metrics

* (backend) add prometheus metrics, add the message by status metric

* (backend) Add more metrics on attachement + add 0 by default for statuses

* 🚨(backend) ignore import-outside-toplevel for collector

* 🚨(backend) add all docstring + don't access private properties

* 🔒️(backend) add basic auth to /metrics route

* 🚸(backend) use labels instead of hard-coding statuses in metric

* 📝(backend) document prometheus

* 🎨(backend) lint

* (backend) add first tests for prometheus

* 🚚(backend) move prometheus endpoint to api/<ver>/prometheus/metrics, change middlewarename

* (backend) add attachment tests for prometheus metrics

* 🐛(backend) actually allow disabling prometheus

* 🎨(backend) improve logic in mw

* 🎨(backend) small review fixes

* 🚨(backend) lint

* 🐛(backend) Remove sha256 from factory

* 🎨(backend) nitpicks + linter fixes

* 🐛(poetry) remove duplicate prometheus package in dev deps

* 🐛(poetry) add missing dep

* 🐛(prom) make tests more reliable, disable by default + misc fixes

* 📝(prom) fix docstring in tests

---------

Co-authored-by: Stanislas Bruhiere <stanislas@bruhiere.fr>
2025-09-05 12:22:54 +02:00
03a8dc743d (mta) add Direct mode for MTA out (#285)
This PR adds support for 2 MTA out modes : Direct-to-MX and SMTP-relay outbound delivery. Direct mode supports SOCKS5 proxies, and we bundle a new `src/socks-proxy` component to support it.

We also add an end-to-end self-check command plus scheduled health-check task with optional Prometheus metrics.

---------

Co-authored-by: Bastien Ogier <bastien.ogier@ext.anct.gouv.fr>
Co-authored-by: Stanislas Bruhiere <stanislas@bruhiere.fr>
2025-09-04 15:48:46 +02:00