diff --git a/README.md b/README.md index db0a438..175ffd0 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,12 @@ docker run --rm -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest Both the backend server and the frontend are written in Rust. The backend receives email over an unencrypted connection on a configurable port. All email is stored in memory while the application is running. An API exposes all received email: -- `/api/messages` return all message metadata -- `/api/message/[id]` returns a complete message, given its `id` -- `/api/version` returns version information about the executable -- `/ws` send email metadata to each connected client when a new email is received +- `GET /api/messages` return all message metadata +- `GET /api/message/[id]` returns a complete message, given its `id` +- `POST /api/delete/[id]` deletes a message, given its `id` +- `POST /api/delete-all` deletes all messages +- `GET /api/version` returns version information about the executable +- `GET /ws` send email metadata to each connected client when a new email is received The frontend initially performs a call to `/api/messages` to receive all existing email metadata and then subscribes for new messages using the websocket connection. When opening a message, the `/api/message/[id]` endpoint is used to retrieve the complete message body and raw email. diff --git a/backend/src/tests.rs b/backend/src/tests.rs index c5b468a..4875894 100644 --- a/backend/src/tests.rs +++ b/backend/src/tests.rs @@ -41,7 +41,7 @@ async fn send_message( .join("\n"); let html: String = format!( "{}\n

external link

", - body.replace("\n", "
\n") + body.replace('\n', "
\n") ); let builder = Message::builder() diff --git a/backend/src/web_server.rs b/backend/src/web_server.rs index 88d1de7..3836ad7 100644 --- a/backend/src/web_server.rs +++ b/backend/src/web_server.rs @@ -6,7 +6,7 @@ use axum::{ }, http::{header, StatusCode, Uri}, response::{Html, IntoResponse, Response}, - routing::get, + routing::{get, post}, Extension, Json, Router, }; use serde::Serialize; @@ -165,6 +165,38 @@ async fn message_body_handler( } } +/// delete a message +async fn message_delete_handler( + Path(id): Path, + Extension(state): Extension>, +) -> Result { + if let Ok(mut storage) = state.storage.write() { + if storage.remove(&id).is_some() { + info!("message {} removed", &id); + + Ok(StatusCode::OK) + } else { + Err(StatusCode::NOT_FOUND) + } + } else { + Err(StatusCode::INTERNAL_SERVER_ERROR) + } +} + +/// delete all messages +async fn message_delete_all_handler( + Extension(state): Extension>, +) -> Result { + if let Ok(mut storage) = state.storage.write() { + storage.clear(); + info!("storage cleared"); + + Ok(StatusCode::OK) + } else { + Err(StatusCode::INTERNAL_SERVER_ERROR) + } +} + /// return version async fn version_handler() -> Result, StatusCode> { let vi = VersionInfo { @@ -220,6 +252,8 @@ pub async fn web_server( .route("/api/messages", get(messages_handler)) .route("/api/message/:id", get(message_handler)) .route("/api/message/:id/body", get(message_body_handler)) + .route("/api/delete/:id", post(message_delete_handler)) + .route("/api/delete-all", post(message_delete_all_handler)) .route("/api/version", get(version_handler)) .nest_service("/static", get(static_handler));