Add bounce mailbox processing
This commit is contained in:
@@ -7,6 +7,7 @@ from sqlalchemy import inspect
|
||||
|
||||
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER
|
||||
from govoplan_core.core.mail import (
|
||||
CAPABILITY_MAIL_BOUNCE_PROCESSING,
|
||||
CAPABILITY_MAIL_DELIVERY_OUTBOX,
|
||||
CAPABILITY_MAIL_NOTIFICATION_DELIVERY,
|
||||
)
|
||||
@@ -46,6 +47,8 @@ _mail_table_retirement_provider = drop_table_retirement_provider(
|
||||
mail_models.MailDeliveryReconciliation,
|
||||
mail_models.MailDeliveryAttempt,
|
||||
mail_models.MailDeliveryCommand,
|
||||
mail_models.MailBounceObservation,
|
||||
mail_models.MailBounceSource,
|
||||
label="Mail",
|
||||
)
|
||||
|
||||
@@ -111,6 +114,16 @@ PERMISSIONS = (
|
||||
"Reconcile mail delivery outcomes",
|
||||
"Reconcile unknown delivery outcomes and explicitly authorize deliberate resend commands.",
|
||||
),
|
||||
_permission(
|
||||
"mail:bounce:read",
|
||||
"View bounce processing",
|
||||
"Inspect bounded delivery-status observations and their correlation state.",
|
||||
),
|
||||
_permission(
|
||||
"mail:bounce:manage",
|
||||
"Manage bounce processing",
|
||||
"Configure and run IMAP delivery-status watchers.",
|
||||
),
|
||||
_permission(
|
||||
"mail:secret:manage_own",
|
||||
"Manage own mail secrets",
|
||||
@@ -132,6 +145,8 @@ ROLE_TEMPLATES = (
|
||||
"mail:secret:manage",
|
||||
"mail:delivery:diagnostic",
|
||||
"mail:delivery:reconcile",
|
||||
"mail:bounce:read",
|
||||
"mail:bounce:manage",
|
||||
),
|
||||
),
|
||||
RoleTemplate(
|
||||
@@ -184,6 +199,7 @@ manifest = ModuleManifest(
|
||||
ModuleInterfaceProvider(name="mail.delivery_commands", version="0.1.0"),
|
||||
ModuleInterfaceProvider(name="mail.delivery_outbox", version="0.1.0"),
|
||||
ModuleInterfaceProvider(name="mail.notification_delivery", version="0.1.0"),
|
||||
ModuleInterfaceProvider(name="mail.bounce_processing", version="0.1.0"),
|
||||
),
|
||||
requires_interfaces=(
|
||||
ModuleInterfaceRequirement(
|
||||
@@ -208,7 +224,7 @@ manifest = ModuleManifest(
|
||||
permissions=PERMISSIONS,
|
||||
route_factory=_mail_router,
|
||||
role_templates=ROLE_TEMPLATES,
|
||||
nav_items=(NavItem(path="/mail", label="Mail", icon="mail", required_any=("mail:mailbox:read",), order=50),),
|
||||
nav_items=(NavItem(path="/mail", label="Mail", icon="mail", required_any=("mail:mailbox:read", "mail:bounce:read", "mail:bounce:manage"), order=50),),
|
||||
frontend=FrontendModule(
|
||||
module_id="mail",
|
||||
package_name="@govoplan/mail-webui",
|
||||
@@ -219,8 +235,15 @@ manifest = ModuleManifest(
|
||||
required_any=("mail:mailbox:read",),
|
||||
order=50,
|
||||
),
|
||||
FrontendRoute(
|
||||
path="/mail/bounces",
|
||||
component="MailBouncePage",
|
||||
required_any=("mail:bounce:read", "mail:bounce:manage"),
|
||||
order=51,
|
||||
surface_id="mail.bounce-processing",
|
||||
),
|
||||
),
|
||||
nav_items=(NavItem(path="/mail", label="Mail", icon="mail", required_any=("mail:mailbox:read",), order=50),),
|
||||
nav_items=(NavItem(path="/mail", label="Mail", icon="mail", required_any=("mail:mailbox:read", "mail:bounce:read", "mail:bounce:manage"), order=50),),
|
||||
view_surfaces=(
|
||||
ViewSurface(id="mail.admin.system-servers", module_id="mail", kind="section", label="System mail servers", order=70),
|
||||
ViewSurface(id="mail.admin.tenant-servers", module_id="mail", kind="section", label="Tenant mail servers", order=60),
|
||||
@@ -248,6 +271,8 @@ manifest = ModuleManifest(
|
||||
mail_models.MailDeliveryReconciliation,
|
||||
mail_models.MailDeliveryAttempt,
|
||||
mail_models.MailDeliveryCommand,
|
||||
mail_models.MailBounceObservation,
|
||||
mail_models.MailBounceSource,
|
||||
label="Mail",
|
||||
),
|
||||
),
|
||||
@@ -261,6 +286,10 @@ manifest = ModuleManifest(
|
||||
"govoplan_mail.backend.capabilities",
|
||||
fromlist=["notification_delivery_capability"],
|
||||
).notification_delivery_capability(context),
|
||||
CAPABILITY_MAIL_BOUNCE_PROCESSING: lambda context: __import__(
|
||||
"govoplan_mail.backend.bounce_processing",
|
||||
fromlist=["SqlMailBounceProcessingProvider"],
|
||||
).SqlMailBounceProcessingProvider(),
|
||||
},
|
||||
documentation=(
|
||||
DocumentationTopic(
|
||||
@@ -300,6 +329,37 @@ manifest = ModuleManifest(
|
||||
],
|
||||
},
|
||||
),
|
||||
DocumentationTopic(
|
||||
id="mail.bounce-processing",
|
||||
title="Delivery-status and bounce processing",
|
||||
summary="Watch an authorized IMAP folder and correlate DSN outcomes with durable Mail commands.",
|
||||
body=(
|
||||
"Mail stores the outgoing RFC Message-ID with its durable command, parses "
|
||||
"bounded message/delivery-status reports without changing mailbox flags, "
|
||||
"and records idempotent per-recipient observations. SMTP acceptance remains "
|
||||
"separate from a later bounce. Unmatched reports remain visible for review; "
|
||||
"Mail stores only bounded diagnostics and a raw digest, not the raw bounce body."
|
||||
),
|
||||
layer="configured",
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("mail_admin", "campaign_manager", "release_reviewer"),
|
||||
order=43,
|
||||
conditions=(
|
||||
DocumentationCondition(
|
||||
required_modules=("mail",),
|
||||
any_scopes=("mail:bounce:read", "mail:bounce:manage"),
|
||||
),
|
||||
),
|
||||
related_modules=("campaigns", "audit"),
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/mail/bounces",
|
||||
"verification": (
|
||||
"Send a message with a unique Message-ID, ingest a DSN twice, and "
|
||||
"verify one correlated observation while the SMTP acceptance remains intact."
|
||||
),
|
||||
},
|
||||
),
|
||||
DocumentationTopic(
|
||||
id="mail.profile-ownership-and-consumers",
|
||||
title="Mail owns transport profiles and credentials",
|
||||
|
||||
Reference in New Issue
Block a user