Commit Graph
68 Commits
Author SHA1 Message Date
jbpenrath 9066c50961 🔧(mobile) per-environment app identity and Play release tooling
A staging and a production build must coexist on one device: the app id,
displayed name and OIDC deep-link scheme become env-driven
(MOBILE_APP_ID / MOBILE_APP_NAME / MOBILE_AUTH_SCHEME) across cap sync,
gradle and Xcode (generated xcconfig) — a divergence between the JS and
native halves strands the login callback silently, and a wrong identity
is frozen forever by the first store upload, so release builds now fail
on any mismatch. mobile-android-release produces the signed Play bundle;
the upload keystore and google-services.json stay per-instance and
gitignored.
2026-08-13 12:14:34 +02:00
jbpenrath fea61e1be2 🔧(mobile) make the app identity and auth scheme env-driven
A staging and a production build must be installable side by side on one
device, and two apps claiming the same OIDC deep-link scheme would make
Android ask the user which one receives the login callback, mid-flow. The
app id, the displayed name and the callback scheme therefore become
per-environment (MOBILE_APP_ID / MOBILE_APP_NAME / MOBILE_AUTH_SCHEME).
2026-08-13 12:08:40 +02:00
jbpenrath 461152179d (jmap_email) clean text preview
Add a `preview_text` helper that is exposed by the lib.
It stripped html tags through a HTMLParser and also strip
markdownish syntax that can be found into text body.
Previously preview attribute could contains html/md noise,
now it is a clean display ready text string.
2026-07-23 11:49:10 +02:00
Sylvain Zimmerandjbpenrath 07e906a390 🔥(testdomain) remove the TESTDOMAIN feature
It was intended for early tests. We can create autojoin domains now instead.
2026-07-22 16:11:19 +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 94d079bc1b (mobile) add self-hosted OTA update chain
Store review cycles make shipping web-layer fixes through the stores
too slow, so the apps update their JS bundle over the air. The chain is
fully self-hosted to keep sovereignty: bundles and channel manifests
live on an anonymous-read S3 bucket (create_bucket --public / the
create-ota-bucket script) and the Capgo plugin is driven entirely from
JS against that manifest (autoUpdate off — no Capgo server involved).
Bundles are RSA-signed at publish time and verified against the
per-instance public key baked in at cap sync, so a tampered zip on the
public bucket is rejected.
Versions use a git-derived <count>-<sha> id stamped into the builtin
bundle so a fresh install does not re-download its own commit, and
channels (dev/staging/prod) are fully independent because
NEXT_PUBLIC_* vars are inlined at build time.
Also ships docs/mobile.md.
2026-07-22 11:21:05 +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
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 Zimmer 1fe34391a8 👷(jmap-email) add PyPI release scripts 2026-06-09 15:44:46 +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
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 Zimmer e6bf029488 🐛(makefile) make sure we create env files before any call to compose
Fixes #674
2026-05-21 23:28:01 +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
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
Sylvain Zimmerandjbpenrath 6eab1af6d6 (rfc5322) switch back to Python's stdlib for email composition
Latest fixes in the stdlib make it a more solid alternative for strict composition than Flanker. We keep Flanker for now for lenient inbound parsing. We add stronger tests and fuzzing to validate we didn't regress.
2026-05-20 18:57:36 +02:00
f002a22dd3 (global) allow user to send internal message through ThreadEvent (#566)
When a Thread has several accesses or is linked to a shared mailbox,
an input allows to post internal messages. It also allows to
mention user in a message. The ThreadEvent model
is the foundation to enrich Threads with further kind of event.

Co-authored-by: Sylvain Zimmer <sylvinus@users.noreply.github.com>
2026-04-01 18:22:30 +02:00
jbpenrath 4f5591b722 (global) store thread read state by thread access
Currently thread sharing is confusing as the read state is shared from
all thread accesses so we deprecate the `read_at` of the Message model
in favor of a `last_read_at` timestamp pointer on the thread access.

Furthermore, on the thread model, we add several date fields to store relevant
dates in which thread should be sorted according to the current view.
2026-03-05 12:10:29 +01:00
jbpenrath 8330c8df6b 🔧(Makefile) add migrations-check command
Add a command to quickly check if there are issues
with django migrations (missing one, conflict)
2026-03-05 09:56:35 +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 8f36b1685d 🔒️(frontend) add defense in-depth for XSS vulnerabilities (#520)
Avoid using HTML strings whenever possible, always try to use the react render to string functions with JSX.
2026-02-05 11:22:39 +01:00
Sylvain ZimmerandGitHub c3560ee739 (parser) add fuzzing tests, fix a few edge cases (#507)
These cases might have caused exceptions, which might have interrupted
some imports.
2026-01-29 11:01:56 +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
Sabrina DemagnyandGitHub 0cb05d9e77 (scripts) add release automation script (#472)
Add a Python script to automate the release process:
- Validates semver version format
- Calculates next version automatically based on release kind
- Updates version in pyproject.toml, package.json and package-lock.json
- Updates CHANGELOG.md with release date
- Creates release branch and commits changes

Usage:
make release
or
bin/release.py --kind m --version ...
2026-01-12 00:37:16 +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 a87d8f4a46 (e2e) add first tests
Add first test to test import and send message

Resolve #238
2025-11-25 23:28:21 +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
Sabrina Demagny 23356c5837 (backend) remove i18n location lines in .po files 2025-11-03 11:54:57 +01:00
jbpenrath cfc12485ed (front) collapse embedded message
Currently threads are hard to read mainly because embedded
replies and other kind of messages are not collapsed like other
mail application does. So we implement a logic to detect empirically
those kind of message and wrap them into a details element.
2025-10-23 19:54:00 +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 ZimmerandGitHub 1c316b7291 (widgets) add La Gaufre widget + improve accessibility (#360)
All other widgets benefit from focus trap, espace key binding, and overall linting
2025-10-07 10:19:14 +02:00
Jean-Baptiste PENRATHandGitHub bf38eaa60b (archive) implement archiving functionality for messages and threads (#363)
We want to be able to archive threads. So we update Thread model to know
if the thread is archived according to its messages state. Then update the
flag endpoint api to archive messages or threads. Also update the search indexer
to be able to search within archived threads. And finally update the frontend to
allow the user to archive a thread, get and search in archived threads.
2025-10-06 18:27:30 +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 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
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 Zimmer 956679e9b2 (dns) add button to copy all DNS records 2025-08-20 16:00:24 +02:00
jbpenrath 7c07711489 👽️(openapi) improve user definition with or without abilities
The OpenAPI Schema does not embedded abilities property.
We fix that and make two serializers for User model in order to get proper
openapi definition for User instance with abilities and without abilities.
2025-07-31 17:19:23 +02:00
Sylvain ZimmerandGitHub 9b4ba9ba09 (dns) add admin page for DNS records (#283)
* (dns) add admin page for DNS records. Fixes #177

* 👷(dockerflow) add a flag to disable Dockerflow

* 💚(ghactions) fix check-api-update that was missing Orval

* 💚(ci) fix tests that depended on Keycloak

* ♻️(admin) refactor CopyableInput into a separate component

* 🎨(dns) improve code style
2025-07-21 14:41:08 +02:00
Sylvain ZimmerandGitHub 8319250fbb ♻️(dns) refactor DNS and Scaleway code to be simpler (#276) 2025-07-17 00:57:33 +02:00
jbpenrath 319918be66 🔧(make) allow to install front deps from make command
`make front-install` now accepts args to install deps from the
docker container with ease.
e.g: `make front-install react` or `make front-install -- -DE @type/react`
2025-07-16 22:22:25 +02:00
Sylvain Zimmer f29ba226e6 🐛(drafts) fix concurrent updates of drafts with blob delete 2025-07-15 23:13:07 +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
jbpenrath c3b40d41f3 🔧(Makefile) add command to check typescript issue
Add a command to run `tsc --noEmit`. It allows to quickly
know if there is ts issues without build the project.
2025-06-30 16:22:25 +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
jbpenrath 328c1616eb 👽️(frontend) update api client
Run `make api-update`
2025-06-15 16:05:35 +02:00