Implement P05 checked services and safe selected delivery recovery
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
This commit is contained in:
parent
99618488e0
commit
ac0eb14b75
15 changed files with 735 additions and 88 deletions
|
|
@ -109,6 +109,7 @@ class PortalApplication:
|
|||
self.provisioning = provisioning
|
||||
self.tenant_management = tenant_management
|
||||
self.outbox_delivery = outbox_delivery
|
||||
self.operations_probe = None
|
||||
self.registration_verification = registration_verification
|
||||
self.registration_clients = frozenset(registration_clients)
|
||||
self.registration_tenants = frozenset(registration_tenants)
|
||||
|
|
@ -791,6 +792,27 @@ class PortalApplication:
|
|||
query = parse_qs(str(environ.get("QUERY_STRING", "")))
|
||||
return self._html(start_response, self._platform_activity(
|
||||
query.get("reference", [""])[0], query.get("tenant", [""])[0]), correlation_id)
|
||||
if path == "/platform/operations/deliver" and method == "POST":
|
||||
self.service.resolve_tenant_context(actor, PLATFORM_TENANT)
|
||||
body = self._form_body(environ)
|
||||
self._require_csrf(environ, body.get("csrf_token", ""))
|
||||
event_id = body.get("event_id", "")
|
||||
with self.service.store.outbox_delivery_guard(event_id):
|
||||
event = self.service.store.outbox_event(event_id)
|
||||
if event is None: raise NotFoundError("delivery record not found")
|
||||
if event.delivered_at:
|
||||
raise ConflictError("Delivery is already completed.")
|
||||
if self.outbox_delivery is None: raise ValidationError("Delivery is unavailable")
|
||||
confirmation = self._confirm_change(environ, start_response, body,
|
||||
json.dumps(_jsonable(event),sort_keys=True), "Attempt this delivery",
|
||||
"This submits only delivery " + event.event_id + " for " + event.tenant +
|
||||
". Invitation messages will be sent to the recorded recipient. Provider acceptance does not prove the person received it. Keep support reference " + event.correlation_id + ".",
|
||||
correlation_id)
|
||||
if confirmation is not None: return confirmation
|
||||
self.service.deliver_outbox(actor, self.outbox_delivery,
|
||||
worker_id="platform-operator",event_id=event.event_id,
|
||||
expected_attempts=event.delivery_attempts,correlation_id=correlation_id)
|
||||
return self._redirect(start_response,"/platform/operations?"+urlencode({"event_id":event_id}),correlation_id)
|
||||
if path in {"/platform/operations", "/platform/operations/replay"}:
|
||||
self.service.resolve_tenant_context(actor, PLATFORM_TENANT)
|
||||
if method == "POST" and path.endswith("/replay"):
|
||||
|
|
@ -799,7 +821,7 @@ class PortalApplication:
|
|||
event = self.service.store.outbox_event(str(body.get("event_id", "")))
|
||||
if event is None:
|
||||
raise NotFoundError("delivery record not found")
|
||||
if event.delivered_at is not None or event.claimed_by:
|
||||
if event.delivered_at is not None or (event.claimed_by and not event.failed_at):
|
||||
raise ConflictError("Delivery is already completed or being processed. Refresh its status.")
|
||||
self.service.replay_outbox(actor, event.event_id, correlation_id=correlation_id)
|
||||
return self._redirect(start_response, "/platform/operations?"+urlencode({"event_id":event.event_id}), correlation_id)
|
||||
|
|
@ -1191,6 +1213,8 @@ class PortalApplication:
|
|||
@staticmethod
|
||||
def _delivery_status(event: Any) -> str:
|
||||
if event.delivered_at: return "Accepted by delivery adapter; receipt by the person is unverified"
|
||||
if event.failure_reason == "delivery_outcome_unknown": return "Mail outcome unknown — check provider evidence before sending another message"
|
||||
if event.failure_reason == "mail_unconfigured": return "Mail is not configured — use assisted setup or restore the mail lane"
|
||||
if event.dead_lettered_at: return "Delivery stopped after repeated failures"
|
||||
if event.failed_at: return "Delivery failed; retry pending"
|
||||
if event.claimed_by: return "Being processed"
|
||||
|
|
@ -1242,15 +1266,19 @@ class PortalApplication:
|
|||
rows = ""
|
||||
for event in events:
|
||||
action = ""
|
||||
if event.delivered_at is None and not event.claimed_by and (event.failed_at or event.dead_lettered_at):
|
||||
action = f'<form method="post" action="/platform/operations/replay"><input type="hidden" name="csrf_token" value="{escape(csrf)}"><input type="hidden" name="event_id" value="{escape(event.event_id)}"><button type="submit">Queue a retry</button></form>'
|
||||
if event.delivered_at is None:
|
||||
action = f'<form method="post" action="/platform/operations/deliver"><input type="hidden" name="csrf_token" value="{escape(csrf)}"><input type="hidden" name="event_id" value="{escape(event.event_id)}"><button type="submit">Review delivery attempt</button></form>'
|
||||
if event.delivered_at is None and (not event.claimed_by or event.failed_at) and (event.failed_at or event.dead_lettered_at):
|
||||
action += f'<form method="post" action="/platform/operations/replay"><input type="hidden" name="csrf_token" value="{escape(csrf)}"><input type="hidden" name="event_id" value="{escape(event.event_id)}"><button type="submit">Queue a retry</button></form>'
|
||||
rows += f'<tr><td>{escape(event.event_id)}</td><td>{escape(event.tenant)}</td><td>{escape(event.event_type)}</td><td>{escape(self._delivery_status(event))}</td><td>{escape(event.correlation_id)}</td><td>{action}</td></tr>'
|
||||
configuration = self._operation_capabilities()
|
||||
return self._page_html("Service recovery", '<h1>Service recovery</h1>' + configuration + '<p><a href="/platform/activity">Investigate a support reference</a></p><p>This view shows local delivery records. Live sign-in, email receipt and authenticator health are not verified here.</p>'
|
||||
from user_engine.operations_status import check_services, selected_mail_status
|
||||
evidence = selected_mail_status(self.outbox_delivery, events[0]) if event_id else ""
|
||||
configuration = (self.operations_probe() if self.operations_probe else check_services(None,None)) + self._operation_capabilities()
|
||||
return self._page_html("Service recovery", '<h1>Service recovery</h1>' + configuration + evidence + '<p><a href="/platform/activity">Investigate a support reference</a></p><p>Delivery records below describe recorded attempts. Component checks do not prove a complete sign-in or inbox receipt.</p>'
|
||||
'<form method="get" action="/platform/operations"><label>Delivery record ID <input name="event_id"></label><button type="submit">Find delivery</button></form>'
|
||||
'<table><thead><tr><th>Delivery</th><th>Tenant</th><th>Kind</th><th>Status</th><th>Support reference</th><th>Recovery</th></tr></thead><tbody>'
|
||||
+ (rows or '<tr><td colspan="6">No delivery records. This does not prove mail was received.</td></tr>')
|
||||
+ '</tbody></table><p>Queued retries are processed by the delivery worker. Check the record again for the result.</p><p><a href="/platform">Return to platform administration</a></p>')
|
||||
+ '</tbody></table><p>Queueing a retry does not send it. Use Review delivery attempt to submit one selected record, then check its result. No background worker is enabled here.</p><p><a href="/platform">Return to platform administration</a></p>')
|
||||
|
||||
def _operation_capabilities(self) -> str:
|
||||
capabilities = (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue