feat: send confirmation email to sender

When users submit a contact form, they now receive a confirmation
email acknowlledging receipt of their message. The backend also
continues to send a notification email to the configured recipient.

If the backend fails to send the acknowledgement email to the sender,
it will assume the email is not valid and will therefore not transmit
the contact request to the configured recipient.

Changes:
- Refactor `send_email()` to `send_emails()` that sends two emails:
  - Confirmation email from the submitter
  - Notification email to the configured recipient
- Add `From<T>` implementations of various errors for new error type
  `ContactError`.
- Errors now return a translation identifier for the frontend.
This commit is contained in:
2025-11-15 14:08:37 +01:00
parent 71c4cf1061
commit 0964843c49
10 changed files with 351 additions and 79 deletions

View File

@@ -143,6 +143,38 @@ pub struct EmailSettings {
pub tls: bool,
}
impl EmailSettings {
/// Parses the sender email address into a `Mailbox` for use with lettre.
///
/// # Errors
///
/// Returns a `ContactError` if the email address in the `from` field cannot be parsed
/// into a valid mailbox. This can occur if:
/// - The email address format is invalid
/// - The email address contains invalid characters
/// - The email address structure is malformed
pub fn try_sender_into_mailbox(
&self,
) -> Result<lettre::message::Mailbox, crate::errors::ContactError> {
Ok(self.from.parse::<lettre::message::Mailbox>()?)
}
/// Parses the recipient email address into a `Mailbox` for use with lettre.
///
/// # Errors
///
/// Returns a `ContactError` if the email address in the `from` field cannot be parsed
/// into a valid mailbox. This can occur if:
/// - The email address format is invalid
/// - The email address contains invalid characters
/// - The email address structure is malformed
pub fn try_recpient_into_mailbox(
&self,
) -> Result<lettre::message::Mailbox, crate::errors::ContactError> {
Ok(self.recipient.parse::<lettre::message::Mailbox>()?)
}
}
impl std::fmt::Debug for EmailSettings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EmailSettings")
@@ -466,9 +498,7 @@ mod tests {
fn startls_try_from_str_invalid() {
let result = Starttls::try_from("invalid");
assert!(result.is_err());
assert!(result
.unwrap_err()
.contains("not a supported option"));
assert!(result.unwrap_err().contains("not a supported option"));
}
#[test]