feat(mail): add governed JMAP mailbox sync and search
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-08-22 17:09:05 +02:00
parent fd808336bc
commit 1cbf4acaf4
29 changed files with 2481 additions and 183 deletions
+33 -8
View File
@@ -24,6 +24,7 @@ from govoplan_mail.backend.db.models import (
SMTP_PROVIDER_ID = "mail.smtp_delivery"
IMAP_PROVIDER_ID = "mail.imap_mailbox"
JMAP_PROVIDER_ID = "mail.jmap_mailbox"
POP3_PROVIDER_ID = "mail.pop3_legacy_import"
_CURRENT_INDEX_WINDOW = timedelta(minutes=30)
@@ -40,6 +41,12 @@ def imap_provider_states(
return _mail_provider_states(context, protocol="imap")
def jmap_provider_states(
context: ExternalProviderStateContext,
) -> tuple[ExternalProviderRuntimeState, ...]:
return _mail_provider_states(context, protocol="jmap")
def pop3_provider_states(
context: ExternalProviderStateContext,
) -> tuple[ExternalProviderRuntimeState, ...]:
@@ -106,15 +113,28 @@ def _mail_provider_states(
)
metrics = _imap_metrics(context.session, profile_ids=profile_ids)
if protocol == "jmap":
metrics = {
profile_id: {
key: value
for key, value in values.items()
if key in {"indexed_folders", "indexed_messages", "last_indexed_at"}
}
for profile_id, values in metrics.items()
}
provider_id = JMAP_PROVIDER_ID if protocol == "jmap" else IMAP_PROVIDER_ID
return tuple(
_imap_state(
profile,
endpoints=endpoints_by_profile.get(profile.id, []),
metrics=metrics.get(profile.id, {}),
observed_at=observed_at,
protocol=protocol,
provider_id=provider_id,
)
for profile in profiles
if endpoints_by_profile.get(profile.id) or _legacy_configured(profile, "imap")
if endpoints_by_profile.get(profile.id)
or (protocol == "imap" and _legacy_configured(profile, "imap"))
)
@@ -327,10 +347,13 @@ def _imap_state(
endpoints: list[MailServerEndpoint],
metrics: dict[str, Any],
observed_at: datetime,
protocol: str = "imap",
provider_id: str = IMAP_PROVIDER_ID,
) -> ExternalProviderRuntimeState:
protocol_label = protocol.upper()
active = bool(profile.is_active) and (
any(item.is_active for item in endpoints)
or (not endpoints and _legacy_configured(profile, "imap"))
or (protocol == "imap" and not endpoints and _legacy_configured(profile, "imap"))
)
indexed_at = _aware(metrics.get("last_indexed_at"))
errors = int(metrics.get("bounce_source_errors", 0))
@@ -353,8 +376,8 @@ def _imap_state(
else "unknown"
)
return ExternalProviderRuntimeState(
provider_id=IMAP_PROVIDER_ID,
binding_ref=f"mail:profile:{profile.id}:imap",
provider_id=provider_id,
binding_ref=f"mail:profile:{profile.id}:{protocol}",
authority_mode="external_mirror",
observed_at=observed_at,
configured=True,
@@ -371,13 +394,13 @@ def _imap_state(
),
last_success_at=indexed_at or _aware(metrics.get("last_bounce_success_at")),
detail=(
"IMAP mailbox access is disabled."
f"{protocol_label} mailbox access is disabled."
if not active
else "IMAP mailbox or bounce-source errors require attention."
else f"{protocol_label} mailbox or bounce-source errors require attention."
if errors
else "IMAP mailbox state has not been indexed yet."
else f"{protocol_label} mailbox state has not been indexed yet."
if indexed_at is None
else "IMAP mailbox index state is available."
else f"{protocol_label} mailbox index state is available."
),
metrics={
"indexed_folders": int(metrics.get("indexed_folders", 0)),
@@ -469,9 +492,11 @@ def _aware(value: object | None) -> datetime | None:
__all__ = [
"IMAP_PROVIDER_ID",
"JMAP_PROVIDER_ID",
"POP3_PROVIDER_ID",
"SMTP_PROVIDER_ID",
"imap_provider_states",
"jmap_provider_states",
"pop3_provider_states",
"smtp_provider_states",
]