diff --git a/Cargo.lock b/Cargo.lock index 4f6c1ae..39be6e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,9 +321,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68064e60dbf1f17005c2fde4d07c16d8baa506fd7ffed8ccab702d93617975c7" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", @@ -1051,9 +1051,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" [[package]] name = "libm" diff --git a/Cargo.toml b/Cargo.toml index dff9bc0..bdb6d18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ tower = { version = "0.4" } tower-http = { version = "0.5", features = [ "compression-full", "sensitive-headers", + "timeout", "trace", "util", ] } diff --git a/src/endpoints/file/delete.rs b/src/endpoints/file/delete.rs index f47a5ac..a36e098 100644 --- a/src/endpoints/file/delete.rs +++ b/src/endpoints/file/delete.rs @@ -9,7 +9,7 @@ pub async fn delete( Query(params): Query, State(state): State, claims: Claims, -) -> GeneralResult { +) -> GeneralResult { db::file::get_permissions(params.file_id, claims.user_id, &state.pool) .await .can_write_guard()?; @@ -24,5 +24,5 @@ pub async fn delete( .await .handle_internal("Error deleting the file")?; - Ok(StatusCode::NO_CONTENT) + Ok(EmptyResponse) } diff --git a/src/endpoints/file/modify.rs b/src/endpoints/file/modify.rs index faacf4f..506ccf7 100644 --- a/src/endpoints/file/modify.rs +++ b/src/endpoints/file/modify.rs @@ -12,7 +12,7 @@ pub async fn modify( State(state): State, claims: Claims, mut multipart: Multipart, -) -> GeneralResult { +) -> GeneralResult { db::file::get_permissions(params.file_id, claims.user_id, &state.pool) .await .can_write_guard()?; @@ -52,5 +52,5 @@ pub async fn modify( .await .handle_internal("Error updating the file")?; - Ok(StatusCode::NO_CONTENT) + Ok(EmptyResponse) } diff --git a/src/endpoints/file/upload.rs b/src/endpoints/file/upload.rs index 15315a7..a01dcb1 100644 --- a/src/endpoints/file/upload.rs +++ b/src/endpoints/file/upload.rs @@ -36,9 +36,19 @@ async fn create_file( pool: &Pool, ) -> anyhow::Result { let (file_id, file) = storage.create().await?; - let (hash, size) = crate::FileStorage::write_to_file(file, field).await?; - db::file::insert(file_id, parent_folder, file_name, size, hash, pool).await?; - Ok(file_id) + let result = async { + let (hash, size) = crate::FileStorage::write_to_file(file, field).await?; + db::file::insert(file_id, parent_folder, file_name, size, hash, pool).await?; + anyhow::Result::Ok(()) + } + .await; + match result { + Ok(()) => Ok(file_id), + Err(err) => { + let _ = storage.delete(file_id).await; + Err(err) + } + } } async fn parse_field( diff --git a/src/endpoints/folder/delete.rs b/src/endpoints/folder/delete.rs index 1c6ba90..267343b 100644 --- a/src/endpoints/folder/delete.rs +++ b/src/endpoints/folder/delete.rs @@ -9,7 +9,7 @@ pub async fn delete( State(state): State, claims: Claims, Query(params): Query, -) -> GeneralResult<()> { +) -> GeneralResult { let root = db::folder::get_root(claims.user_id, &state.pool) .await .handle_internal("Error getting the root folder")?; @@ -31,5 +31,7 @@ pub async fn delete( Ok(()) }) .await - .handle_internal("Error deleting the fodler") + .handle_internal("Error deleting the fodler")?; + + Ok(EmptyResponse) } diff --git a/src/endpoints/permissions/delete.rs b/src/endpoints/permissions/delete.rs index 3f326de..95ccf18 100644 --- a/src/endpoints/permissions/delete.rs +++ b/src/endpoints/permissions/delete.rs @@ -10,7 +10,7 @@ pub async fn delete( State(pool): State, claims: Claims, Query(params): Query, -) -> GeneralResult { +) -> GeneralResult { if params.user_id != claims.user_id { db::folder::get_permissions(params.folder_id, claims.user_id, &pool) .await @@ -21,5 +21,5 @@ pub async fn delete( .await .handle_internal("Error deleting the permissions")?; - Ok(StatusCode::NO_CONTENT) + Ok(EmptyResponse) } diff --git a/src/endpoints/permissions/set.rs b/src/endpoints/permissions/set.rs index c850699..346d8b2 100644 --- a/src/endpoints/permissions/set.rs +++ b/src/endpoints/permissions/set.rs @@ -11,7 +11,7 @@ pub async fn set( claims: Claims, State(pool): State, Json(params): Json, -) -> GeneralResult { +) -> GeneralResult { let root = db::folder::get_root(claims.user_id, &pool) .await .handle_internal("Error getting the root folder")?; @@ -53,5 +53,5 @@ pub async fn set( .await .handle_internal("Error writing to the database")?; - Ok(StatusCode::NO_CONTENT) + Ok(EmptyResponse) } diff --git a/src/endpoints/users/delete.rs b/src/endpoints/users/delete.rs index 898e998..7b3dacf 100644 --- a/src/endpoints/users/delete.rs +++ b/src/endpoints/users/delete.rs @@ -1,14 +1,19 @@ +use std::time::Duration; + use crate::prelude::*; pub async fn delete( State(AppState { pool, ref storage }): State, claims: Claims, -) -> GeneralResult<()> { +) -> GeneralResult { + tokio::time::sleep(Duration::from_secs(100)).await; db::users::delete_user(claims.user_id, &pool) .try_for_each_concurrent(5, |file_id| async move { let _ = storage.delete(file_id).await; Ok(()) }) .await - .handle_internal("Error deleting the user") + .handle_internal("Error deleting the user")?; + + Ok(EmptyResponse) } diff --git a/src/main.rs b/src/main.rs index 78639e0..39d0f7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,8 @@ mod endpoints; mod errors; mod file_storage; mod prelude; +mod util; -use std::{env, net::Ipv4Addr}; - -use axum::Router; use file_storage::FileStorage; type Pool = sqlx::postgres::PgPool; @@ -63,6 +61,8 @@ fn init_tracing() { #[tokio::main] async fn main() -> anyhow::Result<()> { + use std::{env, net::Ipv4Addr}; + if env::var("RUST_BACKTRACE").is_err() { env::set_var("RUST_BACKTRACE", "1"); } @@ -124,7 +124,7 @@ async fn shutdown_signal() { ctrl_c.await; } -fn app(state: AppState) -> Router { +fn app(state: AppState) -> axum::Router { use axum::{ extract::DefaultBodyLimit, handler::Handler as _, @@ -137,6 +137,7 @@ fn app(state: AppState) -> Router { users, }; use tower_http::{ + timeout::TimeoutLayer, trace::{MakeSpan, TraceLayer}, ServiceBuilderExt as _, }; @@ -160,19 +161,22 @@ fn app(state: AppState) -> Router { const TEN_GIBIBYTES: usize = 10 * 1024 * 1024 * 1024; let body_limit = DefaultBodyLimit::max(TEN_GIBIBYTES); - let middleware = tower::ServiceBuilder::new() + let timeout = TimeoutLayer::new(std::time::Duration::from_secs(10)); + + let common_middleware = tower::ServiceBuilder::new() .sensitive_headers([header::AUTHORIZATION, header::COOKIE]) .layer(TraceLayer::new_for_http().make_span_with(SpanMaker)) .compression(); - Router::new() - .route( - "/files", - get(file::download::download) - .post(file::upload::upload.layer(body_limit.clone())) - .delete(file::delete::delete) - .patch(file::modify::modify.layer(body_limit)), - ) + let file_router = axum::Router::new().route( + "/", + get(file::download::download) + .post(file::upload::upload.layer(body_limit.clone())) + .delete(file::delete::delete.layer(timeout)) + .patch(file::modify::modify.layer(body_limit.clone())), + ); + + let general_router = axum::Router::new() .route( "/folders", get(folder::list::list) @@ -200,6 +204,11 @@ fn app(state: AppState) -> Router { .route("/users/search", get(users::search::search)) .route("/users/register", post(users::register::register)) .route("/users/authorize", post(users::login::login)) - .layer(middleware) + .layer(timeout); + + axum::Router::new() + .nest("/files", file_router) + .nest("/", general_router) + .layer(common_middleware) .with_state(state) } diff --git a/src/prelude.rs b/src/prelude.rs index a97b938..c07d725 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -2,6 +2,7 @@ pub(crate) use crate::{ auth::Claims, db::{self, permissions::PermissionExt as _}, errors::{ErrorHandlingExt as _, GeneralError, GeneralResult, ItemNotFoundExt as _}, + util::EmptyResponse, AppState, Pool, }; pub use axum::{ diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..376563a --- /dev/null +++ b/src/util.rs @@ -0,0 +1,11 @@ +use axum::response::IntoResponse; + +use crate::prelude::*; + +pub struct EmptyResponse; + +impl IntoResponse for EmptyResponse { + fn into_response(self) -> axum::response::Response { + StatusCode::NO_CONTENT.into_response() + } +}