feat(email): send a real test message from admin Email #509

Merged
Claude-Run merged 1 commit from feat/BUNYIP-508-admin-test-email-send into main 2026-08-11 02:34:52 +02:00
Member

The admin Email page could only prove that the relay accepts AUTH: Test connection connects, negotiates TLS, authenticates and quits without sending anything, so a relay that authenticates but then refuses or drops mail still looked healthy. A second button, Test email, now sends a real message through the saved SMTP settings to the signed-in admin's own address and reports the relay's own reason on the page when it does not arrive.

POST /v1/admin/email/test-send takes no request body and no recipient parameter: the address comes from the verified admin claims, so the endpoint cannot be pointed at a third party and turned into a relay for arbitrary mail. It resolves the SAVED email_config row through EmailConfig::from_db_row (not unsaved form edits), shares RateLimitConfig::SMTP_TEST with the connection probe since both hammer the same relay, and writes an AuditAction::AdminTestEmailSent row on both the success and failure paths whose metadata carries host/port/tls and ok but never the recipient address. Like the probe it always answers 200 with {ok, message}: a 5xx would be collapsed to "An unexpected error occurred" by ApiError::user_message, hiding the one thing the admin clicked the button to see. The failure stays loud - logged at error with the underlying cause, ok: false is not a value a successful send can produce, and the page renders a red banner.

The message uses a new smtp_test template pair rather than welcome, which states a price and account status that would be fabricated for a test. It names the SMTP host and the UTC send time so the admin can match it to the click, and carries no billing or account language.

The orphan POST /v1/admin/test-email and its send_test_email handler are removed. Nothing called it, it had no rate limit and no audit row, and it mailed the welcome template with a hardcoded 300 cents, so its "test" told the admin they had been charged $3.00. A source-scanning unit test in routes/admin.rs fails the build if the route or the handler name comes back.

#BUNYIP-508

The admin Email page could only prove that the relay accepts AUTH: `Test connection` connects, negotiates TLS, authenticates and quits without sending anything, so a relay that authenticates but then refuses or drops mail still looked healthy. A second button, `Test email`, now sends a real message through the saved SMTP settings to the signed-in admin's own address and reports the relay's own reason on the page when it does not arrive. `POST /v1/admin/email/test-send` takes no request body and no recipient parameter: the address comes from the verified admin claims, so the endpoint cannot be pointed at a third party and turned into a relay for arbitrary mail. It resolves the SAVED `email_config` row through `EmailConfig::from_db_row` (not unsaved form edits), shares `RateLimitConfig::SMTP_TEST` with the connection probe since both hammer the same relay, and writes an `AuditAction::AdminTestEmailSent` row on both the success and failure paths whose metadata carries host/port/tls and `ok` but never the recipient address. Like the probe it always answers 200 with `{ok, message}`: a 5xx would be collapsed to "An unexpected error occurred" by `ApiError::user_message`, hiding the one thing the admin clicked the button to see. The failure stays loud - logged at `error` with the underlying cause, `ok: false` is not a value a successful send can produce, and the page renders a red banner. The message uses a new `smtp_test` template pair rather than `welcome`, which states a price and account status that would be fabricated for a test. It names the SMTP host and the UTC send time so the admin can match it to the click, and carries no billing or account language. The orphan `POST /v1/admin/test-email` and its `send_test_email` handler are removed. Nothing called it, it had no rate limit and no audit row, and it mailed the welcome template with a hardcoded 300 cents, so its "test" told the admin they had been charged $3.00. A source-scanning unit test in `routes/admin.rs` fails the build if the route or the handler name comes back. #BUNYIP-508
feat(email): send a real test message from admin Email
All checks were successful
E2E / PR gate (pull_request) Successful in 23s
Check / fmt + clippy + build + tests (pull_request) Successful in 4m37s
Create release / Create release from merged PR (pull_request) Has been skipped
68d74af636
The admin Email page could only prove that the relay accepts AUTH: `Test connection` connects, negotiates TLS, authenticates and quits without sending anything, so a relay that authenticates but then refuses or drops mail still looked healthy. A second button, `Test email`, now sends a real message through the saved SMTP settings to the signed-in admin's own address and reports the relay's own reason on the page when it does not arrive.

`POST /v1/admin/email/test-send` takes no request body and no recipient parameter: the address comes from the verified admin claims, so the endpoint cannot be pointed at a third party and turned into a relay for arbitrary mail. It resolves the SAVED `email_config` row through `EmailConfig::from_db_row` (not unsaved form edits), shares `RateLimitConfig::SMTP_TEST` with the connection probe since both hammer the same relay, and writes an `AuditAction::AdminTestEmailSent` row on both the success and failure paths whose metadata carries host/port/tls and `ok` but never the recipient address. Like the probe it always answers 200 with `{ok, message}`: a 5xx would be collapsed to "An unexpected error occurred" by `ApiError::user_message`, hiding the one thing the admin clicked the button to see. The failure stays loud - logged at `error` with the underlying cause, `ok: false` is not a value a successful send can produce, and the page renders a red banner.

The message uses a new `smtp_test` template pair rather than `welcome`, which states a price and account status that would be fabricated for a test. It names the SMTP host and the UTC send time so the admin can match it to the click, and carries no billing or account language.

The orphan `POST /v1/admin/test-email` and its `send_test_email` handler are removed. Nothing called it, it had no rate limit and no audit row, and it mailed the welcome template with a hardcoded 300 cents, so its "test" told the admin they had been charged $3.00. A source-scanning unit test in `routes/admin.rs` fails the build if the route or the handler name comes back.

#BUNYIP-508
Claude-Run deleted branch feat/BUNYIP-508-admin-test-email-send 2026-08-11 02:34:52 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!509
No description provided.