Commit Graph
49 Commits
Author SHA1 Message Date
jbpenrath e36926074f 🔧(frontend) revert linked npm install-strategy
The pnpm-style layout bakes install-local .store/<hash> paths into the
native project files committed by cap sync, and the named node_modules
volume masked the host tree — breaking the IDE and the Gradle/Xcode
builds that resolve Capacitor plugins through relative paths into
src/frontend/node_modules. Back to the hoisted layout installed in the
bind mount, with the bootstrap check updated accordingly.
2026-08-13 12:14:34 +02:00
jbpenrath bc966355b5 (global) clean thread snippet and display message snippet
Use the new preview_text method of jmap_email to generate
clean snippet for thread (denormalized at update_stats)
and message (at serialization). Now when a mesage is folded, we
display this snippet. In the thread list we also display this snippet above
the subject.
2026-08-13 12:08:40 +02:00
BastienandGitHub bfe9492509 ⬆️(chore) bump keycloak to 26.7.1 (#776) 2026-08-08 10:00:53 +02:00
jbpenrath 5ab49f78d4 ⬆️(backend) upgrade to jmap-email 0.3.0
The 0.3.0 parser refuses what 0.1.0 truncated and the composer raises
where it silently mangled, so the app has to take a position at each
seam: a ComposeError on send becomes a 400 (a property of the draft,
not a server fault), an unparseable inbound message is abandoned
outright instead of retried for 48h (deterministic failure — logged
at error level since abandoned rows are purged after 7 days), and a
stored message the stricter parser now refuses is flagged unreadable
to the UI rather than rendered blank. Attachment display names move
to a single service so serializer, blob download and draft builder
synthesize the same name for a nameless MIME part — the bug that
started this branch. The inbound retry sweep gains age-based backoff
so a dependency outage is not polled harder the longer it lasts. The
dev compose mounts the jmap-email working tree over the installed
wheel so local edits propagate without a rebuild.

Archive reconstruction (PST) composes with allow_smtputf8: an EAI
address is legal in an Exchange archive and the reconstructed .eml is
stored, never retransmitted, so refusing it would exclude the message
from the import. The unquote-message reply patterns bound every
whitespace quantifier that could cross newlines: under the m flag an
unbounded \s* backtracks once per line start, quadratic in the line
count of an attacker-supplied body.
2026-08-06 00:19:02 +02:00
Sylvain Zimmerandjbpenrath 19cfa2dcfc (push) add Push Notifications system for iOS, Android, Web 2026-07-22 16:11:18 +02:00
Sylvain Zimmerandjbpenrath f4d235825f ️(devx) improve "make bootstrap" setup time and overall DevX
Notably, we try to reduce disk usage by standardizind on common
base Docker images. We also improve node_modules by reducing
duplicate dependencies and install speed.
2026-07-22 11:34:27 +02:00
jbpenrath ace7991084 (frontend) bootstrap Capacitor mobile apps
Ship the existing SPA as native iOS/Android apps without forking the
codebase: Capacitor wraps the web build, and every mobile-specific
behavior is gated behind isNativePlatform() so the web app is
untouched. The native shells route fetch/cookies through the native
HTTP layer (CapacitorHttp) — the WebView cookie jar is unreliable for
cross-origin sessions — which is why login runs in the system browser
(cross-app SSO via the shared IdP cookie) and finishes through the
backend session handoff, with the deep-link scheme pinned by
sso-invariants tests. Downloads/share go through the Filesystem/Share
plugins since WebView navigation would lose the session.
2026-07-22 11:21:02 +02:00
Sylvain Zimmerandjbpenrath 632f38da2d (mta-in) rewrite MTA-in in pure Python to remove dep on Postfix (#692)
Postfix was already removed as a mta-out dependency, this is the second step so we have a pure python, more auditable path for incoming emails. We plan to keep postfix as a compatible option for a while but it won't be the default once this is battle tested.
2026-07-06 16:20:51 +02:00
BastienandGitHub e16ff27abd ⬆️(keycloak) upgrade theme to 2.3.4 (#732) 2026-06-30 11:01:36 +02:00
BastienandGitHub fc0ff1b354 🔒️(chore) bump keycloak to 26.6.4 (#729)
https://www.cert.ssi.gouv.fr/avis/CERTFR-2026-AVI-0815/
2026-06-29 16:15:46 +02:00
BastienandGitHub 11d191d967 (chore) bump keycloak to 26.6.3 (#718) 2026-06-23 11:51:35 +02:00
Jean-Baptiste PENRATHandGitHub ca04508b56 🔧(deps) install jmap-email from pypi (#711)
Now that jmap-email 0.1.0 is available on pypi we install it from
this registry and remove all tweaks to install the deps from local
folder. We keep the volume override for backend services in order
to be able to work on jmap-email and test it with ease in local
development environment.
2026-06-15 13:45:23 +02:00
Sylvain ZimmerandGitHub 1f33b84381 ♻️(rfc5322) move email parser & composer to new jmap-email lib (#700)
New standalone email library for parsing/composing with strict validation and helpers
2026-06-09 14:41:46 +02:00
Sylvain Zimmerandjbpenrath 3576968e38 (admin) add mandatory TOTP field + search field (#667)
To enable syncing a role to many users, we had to add a custom
Keycloak plugin.
2026-05-20 20:48:00 +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
BastienandGitHub e8d4a00817 (chore) bump keycloak to 26.6.1 (#637) 2026-04-19 15:59:02 +02:00
BastienandGitHub 00e38f5532 (chore) bump keycloak to 26.5.4 (#571) 2026-02-26 11:27:53 +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
BastienandGitHub 100f671e1e (chore) bump keycloak to 26.5.3 (#543) 2026-02-16 22:36:12 +01:00
Sylvain ZimmerandGitHub 78596bb34e (calendar) display calendar invites (#481)
Fixes #433.
2026-01-29 01:06:26 +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
BastienandGitHub a67fd36c33 (ops) implement production docker images (#451)
* (ops) implement production docker images

Github workflows:
  - moved docker-publish jobs into a separate file on push main only
Backend:
  - added healthcheck on backend Dockerfile + minor fixes
  - bumped django to 5.1.15 to mitigate major CVE
  - moved /healthz endpoint to /__heartbeat__ + added db connection
    check
Frontend:
  - Added runtime-prod Dockerfile target + minor fixes on the Dockerfile
  - bump next to 15.5.9 to mitigate major CVE
  - moved nginx config to standard nginx container configuration
    template, fixed `scalingo_postfrontend` accordingly
Keycloak:
  - Added production Dockerfile
  - Removed scalingo_pgdump script & cron.json
socks-proxy:
  - added package upgrades run into runtime Dockerfile stage
Misc:
  - removed scalingo_pgdump.sh & cron.json
  - fixed compose and e2e nginx configs with /healthz replacement

* (fix) coderabbit recommandations
2026-01-12 15:18:25 +01:00
Sylvain ZimmerandGitHub 6a1df09848 (spam) add rspamd integration, header rules and InboundMessage queue (#436)
This adds an intermediate Postgres-backed queue for inbound messages, that 
allows us to run filters like spam processing before inserting messages in their
final storage (soon to be object storage). Also include misc. refactorings.
2025-12-15 23:50:54 +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
Sylvain Zimmer 4833968eb2 🐛(tests) fix failing tests after env var rename 2025-10-18 00:00:09 +02:00
Sylvain Zimmer 7d4cd48669 🚚(widgets) move widgets to integration repository
Here: https://github.com/suitenumerique/integration/pull/33
2025-10-14 22:08:21 +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
Sylvain Zimmer 1e88671b82 🐛(crowdin) fix crowdin env file 2025-10-07 17:01:01 +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
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 Zimmer ce333fd280 🐛(celery) add default queue to worker CLI 2025-09-19 14:12:45 +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
Stanislas BruhièreandGitHub 38439b95ce Improve development docker compose (#324)
* 🐛(docker) stop ALL services when running make stop

* (docker) make sure backend is healthy before exiting docker compose up

* 🧑‍💻 (docker) use default user and not root for healthchecks

* (backend) Remove dockerflow

* (backend) add healthcheck route, and suppress healthcheck access logs

* 🚨(backend) ignore monkey patching

* 🚨(backend) coderabbitai + linter fixes

* 🚚(backend) remove version in healthz route

* 🔨(docker) use python http lib instead of curl and remove curl from dockerfile
2025-09-02 12:10:52 +02:00
Sylvain ZimmerandGitHub 8319250fbb ♻️(dns) refactor DNS and Scaleway code to be simpler (#276) 2025-07-17 00:57:33 +02:00
Sylvain ZimmerandGitHub f1a89a5bdb (all) refactor several areas before 0.1 release (#265)
We are going to release a 0.1 version soon, along with our first production deployment. Starting from there, migrations and a consistent developer experience will be officially supported. To make that easier, this large patch cleans up several areas:
 * Reset migrations one last time
 * Update models for storage efficiency (move textchoices to integerchoices on high-volume tables)
 * Use Blobs for mail mime data and draft bodies. Having them in a separate PG table is a first step, we will later start offloading them to object storage.
 * Add default ZSTD compression to blobs
 * Add per-domain DKIM Keys
 * Add DNS check and provisioning, with a first Scaleway provider
 * Fix Keycloak user provisioning
 * Fix Attachment storage, they are now stored individually only at the drafting stage. Afterwards they are extracted from the main blob. This may be optimized later but at least we only store once. For JMAP compatibility, this requires using fake IDs in the blob API route.
 * Add a management command and recurring task to retry unsent messages
 * Improve the local developer experience with new ports and make commands
 * Repackage MTA-in and MTA-out to be closer to Backend: Poetry, multi-step Dockerfile, move compose and makefile to the root
 * Migrate to OpenSearch
 * Improve overall documentation and add a self-hosting page

Contributes to #177 and #185
2025-07-15 10:41:55 +02:00
Sylvain ZimmerandGitHub e74ceaa66a (mta-in) move Postfix connection to the MDA in a Milter plugin (#248)
This replaces the two plugins we had (check recipients + delivery)
to a single Milter plugin. The real change is that now the delivery
happens during the SMTP session, and can result in a temporary
failure to the client. This makes the MTA-in entirely stateless.
2025-07-07 14:42:46 +02:00
Sylvain Zimmer 0912f4f660 🐛(keycloak) fix Script issue with Keycloak realm import 2025-06-16 16:23:05 +02:00
Sylvain Zimmer 577ad703ca 👷(build) add optionalDependencies for rollup
This allows the same package.json to be used for arm64 and amd64
2025-06-16 10:34:52 +02:00
Sylvain ZimmerandGitHub 460068b5d3 (identity) add MailDomain management with Keycloak identity provider (#136)
This allows user creation, passwords reset and listing users from
Keycloak, plus a new MailDomainAccess model for admins.

* (admin) add maildomainacess model, api route and backend tests

* ♻️(drf) simplify API code

* (keycloack) add deployable keycloak to PaaS

* (keycloak) add integration via API, upgrade to 26.x

* 🗑️(settings) remove invitation setting

* 🐛(tests) fix failing tests after rebase

* 🚨(all) fix lint
2025-06-16 10:03:40 +02:00
Sylvain Zimmer dc0c578e65 🚀(scalingo) deploy worker process on scalingo and fix ES scripts 2025-05-17 10:05:54 +02:00
Jean-Baptiste PENRATHandGitHub 3c16aa3efa 🔧(docker) update celery-dev service build context and target (#90)
Currently the `celery-dev` service is not able to build as the context is wrong
so the Dockerfile cannot be found. Furthermore we also fix the target.
2025-05-13 10:00:42 +02:00
Sylvain ZimmerandGitHub 1f4047a061 (celery) add Celery worker, search, recipient statuses, better build (#88)
Before, sending was done synchronously on the api/send POST call. Now
we just build the MIME message and mark the message for sending, and
queue the actual SMTP (or internal) sending for the worker. We use
this opportunity to add fine-grained delivery statuses for each
recipient, something that traditional email systems are unable to do!

Expanding on that, this (too large) PR adds an initial Elasticsearch
index of threads. It also overhauls the build and CI system, using
Poetry instead of Pip.
2025-05-13 00:52:48 +02:00
Sylvain Zimmer 5bdc8ccf48 🐛(dkim) fix invalid DKIM signature and add verify test
There was an extra \r\n added, that broke validation. We now test
the source of the received message on the mailcatcher, and validate
that DKIM header. Should be much more robust even if it adds a new
dependency for tests.
2025-04-23 00:19:20 +02:00
Sylvain Zimmer d789bb48ca (e2e) make end-to-end emailing work!
You can now send emails to the inbound MTA, which will forward them
to the Django MDA. You can then read them (served through REST) from
the frontend and compose a reply! The frontend will POST the message
to Django, which will build an RFC5322 message and send it to the
outbound MTA, which will relay it to the local mailcatcher where you
can view your reply. Phew!

Initial documentation in the README but this is the core loop that
will be expanded upon, with diagrams and videos!
2025-04-22 18:47:35 +02:00
jbpenrath 1149bf73b7 🔧(make) add commands to generate openapi stuff
Add a command `api-update` to generate JSON OpenApi Schema then
update the frontend api client.
2025-04-22 11:59:10 +02:00
Sylvain Zimmer e522acdef0 👷(front) update frontend tooling, nginx config and app name
Simplified Docker setup for frontend, use frontend-dev for running
the app itself, and frontend-tools for all npm usage. The local
node_modules directory should be used, without any copy in the container
2025-04-17 00:25:53 +02:00
Sylvain ZimmerandGitHub 1070c1ab19 🚀(scalingo) add Scalingo deployment scripts (#12)
* 🚀(scalingo) add scalingo nginx config

* 👷(frontend) fix frontend build, no lint for now

* 👷(scalingo) fix frontend build location and backend buildpack compat

* 🐛(nginx) try another build location

* 🐛(scalingo) fix nginx startup

* 🐛(scalingo) fix scripts and buildpack run location

* 🐛(scalingo) fix buildpack cleanup

* 🐛(nginx) pass host headers to the backend

* 🚀(scalingo) support DATABASE_URL env var
2025-04-16 18:17:09 +02:00
Sylvain Zimmer 287169118b (mta) harmonize MTA and Django routes and tests
First emails can now be sent from port 25 of mta-in through Django.
2025-04-16 01:20:08 +02:00
Sylvain Zimmer 53db5a748d 🎉(all) bootstrap the Messages project
We used the Drive repository as starting point. There are still
some leftovers that will be cleaned up over time.
2025-04-13 18:48:04 +02:00