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:
@@ -4,21 +4,14 @@
|
||||
//! Algorithm (GCRA) via the governor crate. It stores rate limiters in memory
|
||||
//! without requiring external dependencies like Redis.
|
||||
|
||||
use std::{
|
||||
net::IpAddr,
|
||||
num::NonZeroU32,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use std::{net::IpAddr, num::NonZeroU32, sync::Arc, time::Duration};
|
||||
|
||||
use governor::{
|
||||
Quota, RateLimiter,
|
||||
clock::DefaultClock,
|
||||
state::{InMemoryState, NotKeyed},
|
||||
Quota, RateLimiter,
|
||||
};
|
||||
use poem::{
|
||||
Endpoint, Error, IntoResponse, Middleware, Request, Response, Result,
|
||||
};
|
||||
use poem::{Endpoint, Error, IntoResponse, Middleware, Request, Response, Result};
|
||||
|
||||
/// Rate limiting configuration.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -113,7 +106,9 @@ impl<E: Endpoint> Endpoint for RateLimitEndpoint<E> {
|
||||
"Rate limit exceeded"
|
||||
);
|
||||
|
||||
return Err(Error::from_status(poem::http::StatusCode::TOO_MANY_REQUESTS));
|
||||
return Err(Error::from_status(
|
||||
poem::http::StatusCode::TOO_MANY_REQUESTS,
|
||||
));
|
||||
}
|
||||
|
||||
// Process the request
|
||||
@@ -125,7 +120,9 @@ impl<E: Endpoint> Endpoint for RateLimitEndpoint<E> {
|
||||
impl<E> RateLimitEndpoint<E> {
|
||||
/// Extracts the client IP address from the request.
|
||||
fn get_client_ip(req: &Request) -> Option<IpAddr> {
|
||||
req.remote_addr().as_socket_addr().map(std::net::SocketAddr::ip)
|
||||
req.remote_addr()
|
||||
.as_socket_addr()
|
||||
.map(std::net::SocketAddr::ip)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +160,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn rate_limit_middleware_allows_within_limit() {
|
||||
use poem::{handler, test::TestClient, EndpointExt, Route};
|
||||
use poem::{EndpointExt, Route, handler, test::TestClient};
|
||||
|
||||
#[handler]
|
||||
async fn index() -> String {
|
||||
@@ -185,7 +182,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn rate_limit_middleware_blocks_over_limit() {
|
||||
use poem::{handler, test::TestClient, EndpointExt, Route};
|
||||
use poem::{EndpointExt, Route, handler, test::TestClient};
|
||||
|
||||
#[handler]
|
||||
async fn index() -> String {
|
||||
|
||||
Reference in New Issue
Block a user