Commit Graph
331 Commits
Author SHA1 Message Date
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
Sylvain ZimmerandGitHub 6bd7292460 ✨(metrics) new API route for storage usage metrics (#538)
Will be queried by deploy center
2026-02-08 17:41:04 +01:00
jbpenrath 8a6200e3d5 ♻️(e2e) move specific e2e management commands into a dedicated app
Move e2e management command into a dedicated app only installed in e2e
environment to not expose those commands in other environment.
2026-02-05 19:10:12 +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 55993a9780 ✨(dns) stronger DNS checks + configurable records (#522)
adds "Insecure" and "Conflicting" DNS status badges and messages with new checks, and introduces a new MESSAGES_DNS_RECORDS setting.  Fixes #482.
2026-02-05 11:15:42 +01:00
Jean-Baptiste PENRATHandGitHub 021ee22e75 🐛(backend) delete orphan attachments when removed from draft (#532)
When users remove attachments from drafts, only the M2M relationship
was being severed, leaving Attachment and Blob records orphaned in the
database. Over time, this leads to unnecessary storage consumption and
data accumulation.

A management command is also provided to clean up existing orphans.
2026-02-05 10:56:03 +01:00
jbpenrath 25e28cf69d 🔖(minor) release version 0.2.0
Update all version files and changelog for minor release.
2026-02-03 18:16:25 +01:00
ee32351c81 🌐(i18n) update translated strings (#527)
Update translated files with new translations

Co-authored-by: jbpenrath <9265241+jbpenrath@users.noreply.github.com>
2026-02-03 18:16:25 +01:00
Jean-Baptiste PENRATHandGitHub a1b05b51a1 🚑(parser) strip NUL bytes from email content (#524)
Introduced a new helper function to remove NUL bytes from parsed email subjects
and body content, ensuring compatibility with PostgreSQL text fields.
2026-02-03 15:51:11 +01:00
Sylvain ZimmerandGitHub 314e0857d4 🐛(dns) raise new "DUPLICATE" error when there are 2 SPF records (#521) 2026-02-01 21:40:51 +01:00
Jean-Baptiste PENRATHandGitHub b830755943 🚑(importer) fix memory leak with large mbox file import (#516)
Refactored the MBOX file processing to first scan for message positions
without loading the entire file into memory, improving efficiency.
The second pass now streams messages using pre-computed positions,
ensuring memory usage is minimized while maintaining correct
message order for threading.
2026-01-29 16:03:20 +01:00
Sylvain Zimmer 76c8dd32ba 🐛(scalingo) fix env var still overriding the Celery default 2026-01-29 13:56:19 +01:00
Sylvain Zimmer af83580a17 🐛(worker) make celery app name explicit for fix potential $APP override 2026-01-29 13:19:31 +01:00
Jean-Baptiste PENRATHandGitHub 913a5d0442 🔧(backend) use redis cache for development (#515)
By default the default cache backend was a dummy one.
Since we store pending task into cache, this backend does not
allow to track tasks so task api endpoint was always return a 403 error.
2026-01-29 11:11:54 +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 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
Jean-Baptiste PENRATHandGitHub bd76a081e9 ✨(frontend) forward attachments (#485)
When a user forward a message, attachments are now also forwarded. Also,
if some attachments has a cid and embdedded into the html body, this link is
preserved.
2026-01-25 12:06:52 +01:00
Sabrina DemagnyandGitHub 4e7a2a5db1 🔊(inbound) fix duplicate recipient creation errors (#496)
Inbound messages with duplicate recipient addresses are now handled gracefully, preventing duplicate recipient links and avoiding failures during import.
2026-01-24 22:07:25 +01:00
20538a41d0 Fix SSL error and improve authentication failure (#495)
* 🐛(importer) add STARTTLS support and enforce encrypted IMAP connections

- Add STARTTLS negotiation for port 143 with use_ssl=True
- Create IMAPSecurityError exception for security violations
- Refuse to continue unencrypted when SSL is required
- Check server STARTTLS capability before attempting upgrade
- Fail fast to prevent credential leakage on security downgrade

Connection behavior:
- Port 993 + use_ssl=True → SSL direct
- Port 143 + use_ssl=True → STARTTLS (mandatory)
- Port 143 + use_ssl=False → unencrypted (explicit choice)

Fixes: SSL WRONG_VERSION_NUMBER error on port 143

* 🐛(importer) improve IMAP connection and authentication error handling

Backend improvements:
- Catch SSL handshake failures during IMAP4_SSL connection
- Provide clear error message suggesting port 143 with STARTTLS
- Add authentication failure logging with username context
- Wrap login() in try-except to log auth errors before propagating

Frontend improvements:
- Detect "IMAP authentication failed" message in error responses
- Display user-friendly credential error message in UI

This helps users understand connection failures and authentication
issues with clearer, actionable error messages.

* 🐛(imap) add tests and small fixes in the handshake

* 🐛(imap) wrap all connection login in a try/finally for cleanup

---------

Co-authored-by: Sylvain Zimmer <sylvain@sylvainzimmer.com>
2026-01-24 22:02:55 +01:00
Sylvain ZimmerandGitHub 1ce35a4580 ✨(admin) add inboundmessage admin view & other small admin fixes (#505) 2026-01-24 17:02:28 +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 a98f873c63 ✨(backend) add admin message retry action and delivery status filter (#499)
- Allow to filter Message by recipient delivery status
- Allow to trigger retry send task in django admin
2026-01-22 18:11:44 +01:00
jbpenrath c33908f5df 🔖(patch) release version 0.1.1
Update all version files and changelog for patch release.
2026-01-22 10:10:16 +01:00
b625507861 🌐(i18n) update translated strings (#501)
Update translated files with new translations

Co-authored-by: jbpenrath <9265241+jbpenrath@users.noreply.github.com>
2026-01-22 10:10:16 +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
jbpenrath cd261132e7 🔖(minor) release version 0.1.0
Update all version files and changelog for minor release.
2026-01-20 16:01:29 +01:00
1636e06688 🌐(i18n) update translated strings (#494)
Update translated files with new translations

Co-authored-by: jbpenrath <9265241+jbpenrath@users.noreply.github.com>
2026-01-20 15:54:35 +01:00
Sabrina Demagny ec4c7efc74 🔧(settings) by default activate message template 2026-01-16 10:06:32 +01:00
Sabrina Demagny 1899fd80d7 ✨(signature) allow to create mailbox signature 2026-01-16 10:06:32 +01:00
jbpenrath f05caa23fd 🚸(backend) return spam message when looking for trashed message
By default spam and trashed messages are excluded from thread search result.
But there was an edge case when the user was look up within trashed message as
trashed spam message were not returned that was weird. So now spam message are
included in results if user is looking for trashed messages.
2026-01-14 13:50:20 +01:00
jbpenrath 9614e9107b ✨(global) mark thread as spam
Allow users to report a thread as a spam.
Also allow the user to mark the thread has not spam. Then in case of spam,
disable the attachment download for security purpose.
2026-01-14 13:50:20 +01:00
Jean-Baptiste PENRATHandGitHub a19b8dead2 ✨(widget) bind referer netloc within message subject (#478)
Parse HTTP_REFERER to extract the origin name for message subjects if available
2026-01-14 09:54:31 +01:00
888ff9e42d 🐛(rfc5322) improve email parsing & display multiparts in frontend (#463)
* 🩹(mime) fix inline HTML being treated as attachment

Previously, any MIME part with a Content-Disposition header (including
'inline') was treated as an attachment. This caused HTML email bodies
with 'Content-Disposition: inline' to appear as downloadable attachments
instead of being rendered as the message body.

The fix clarifies the classification logic:
- Parts with 'Content-Disposition: attachment' are always attachments
- Parts with 'Content-Disposition: inline' and text/plain or text/html
  are treated as message body
- Parts with filenames but no disposition follow existing rules

* ♻️(mime) refactor parser to use Flanker's built-in methods

Replace manual disposition parsing with Flanker's is_attachment(),
is_body(), and is_inline() methods which properly implement RFC standards.

This fixes issues where:
- text/calendar parts were incorrectly treated as attachments
- inline HTML was misclassified
- Parts without disposition were not properly categorized

The new logic uses Flanker's built-in classification:
- is_attachment(): Content-Disposition is 'attachment'
- is_body(): No filename AND (text/* or message/*)
- is_inline(): Content-Disposition is 'inline'

* fix: Add missing disposition_header variable definition

* 🛡️(mime) add defensive exception handling for MIME classification

Add try/except blocks around Flanker's is_attachment(), is_body(), and
is_inline() methods to prevent malformed MIME from crashing the parser.
When classification fails, log the error and safely fall back to treating
all checks as False, allowing parsing to continue.

Also preserve actual Content-Type for attachments instead of forcing
text/plain to application/octet-stream, and infer appropriate file
extensions from MIME types when no filename is provided.

* ♻️(mime) refactor attachment handling with helper functions

Extract duplicate attachment construction logic into reusable helper
functions and add consistent filename extension inference across all
attachment types (attachment, inline, and fallback paths).

Changes:
- Add _infer_filename_from_content_type() to map MIME types to file
  extensions for better unnamed attachment handling
- Add _build_attachment_dict() helper to eliminate ~45 lines of
  duplicated attachment dict construction code
- Apply extension inference consistently to is_attachment, is_inline,
  and fallback branches
- Update tests to expect new filename format (e.g., "unnamed.pdf")
- Clarify exception handler comment for better code documentation

* ✅(rfc5322) fix tests & lint

* 🐛(rfc5322) sanitize attachment filenames and add many more tests

* 🐛(rfc5322) improve RFC5322 parsing logic with nested parts, per JMAP spec

* ✨(rendering) render multipart messages in frontend

* fixup! ✨(rendering) render multipart messages in frontend

* 🐛(review) address PR review comments

- Fix _sanitize_filename to preserve file extension when truncating
- Fix MessageBodyItemSerializer: use required fields, remove Meta class
- Fix misleading comment about text body drive attachments
- Regenerate OpenAPI schema and frontend API client

* 🐛(types) fix null fallback

* fix lint and tests

---------

Co-authored-by: Riël Notermans <riel@zzapps.nl>
Co-authored-by: Riël Notermans <riel@mosa.cloud>
Co-authored-by: jbpenrath <jb.penrath@gmail.com>
2026-01-14 01:18:32 +01:00
Jean-Baptiste PENRATHandGitHub bef485a7fd ✨(search) improve recipient search filters (#476)
- `to` query key now looks up within `to`, `cc` and `bcc` message fields
- Add a `to_exact` query key to looks up only in `to` message field

Resolve #467
2026-01-12 19:18:09 +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
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 b6394a2f56 ✨(limits) implement MAX_RECIPIENTS_PER_MESSAGE (#474)
This is mostly cherrypicked from the larger PR #459, to reduce
complexity. We might still implement the rest in the future.
2026-01-12 12:09:52 +01:00
Sylvain Zimmer fc744543bc 🔒️(errors) avoid serializing Python error externally 2026-01-10 15:16:37 +01:00
Sylvain Zimmer 6558bf88a7 🐛(selfcheck) fix warning from a race condition in thread deletion 2025-12-18 15:03:01 +01:00
Jean-Baptiste PENRATHandGitHub 0e94d33736 👔(backend) add flag is_trashed to thread resource (#454)
Add a flag `is_trashed` to Thread model in order to monitor when a thread is
fully trashed. It helps to exclude those kind of threads in some views with
inactive message like archive or spam for example.

By default we exclude those kind of threads from results until user is looking
for trashed messages.
2025-12-17 10:06:13 +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
Jean-Baptiste PENRATHandGitHub b1eb39c641 ✨(search) add spam folder and search functionality (#452)
Display the SPAM folder in the mailbox panel and allow to search SPAM
messages. Unlike other message flags, SPAM messages are not included
in search result by default
2025-12-11 09:31:59 +01:00
Jean-Baptiste PENRATHandGitHub 3b6f0b9f02 👔(backend) update logic to retrieve drive main_workspace (#449)
The previous route we were using to retrieve the user's Drive main workspace
through drive resource server is not working anymore. But we are able to
retrieve this workspace through the `/me` api route.
2025-12-10 14:17:16 +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
295ffbb3cf 🐛(backend) disable pagination for thread messages
Long threads (more than 20 messages) only displayed the first 20 messages
because the MessageViewSet used the default PAGE_SIZE of 20. The frontend
doesn't handle pagination for messages, so additional messages were silently
hidden from users.

This fix disables pagination for the messages endpoint since threads typically
don't have enough messages to require pagination.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 10:59:51 +01:00
jbpenrath e616cefa8d 👔(backend) do not trigger ai features at delivery on import
On import, the `deliver_inbound_messages` triggers ai features (auto labels and
summarization) if they are enabled. But we don't want to trigger those features
on import.
2025-12-02 15:13:21 +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