Compare commits

..

No commits in common. "e92cebfad7a7c99833942edd97f110470c8d5214" and "a3e4ac2b2e27678c8298356b0c905a338d30885b" have entirely different histories.

13 changed files with 44 additions and 85 deletions

8
Cargo.lock generated
View File

@ -321,9 +321,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.1.13" version = "1.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" checksum = "68064e60dbf1f17005c2fde4d07c16d8baa506fd7ffed8ccab702d93617975c7"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@ -1051,9 +1051,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.156" version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]] [[package]]
name = "libm" name = "libm"

View File

@ -48,7 +48,6 @@ tower = { version = "0.4" }
tower-http = { version = "0.5", features = [ tower-http = { version = "0.5", features = [
"compression-full", "compression-full",
"sensitive-headers", "sensitive-headers",
"timeout",
"trace", "trace",
"util", "util",
] } ] }

View File

@ -9,7 +9,7 @@ pub async fn delete(
Query(params): Query<Params>, Query(params): Query<Params>,
State(state): State<AppState>, State(state): State<AppState>,
claims: Claims, claims: Claims,
) -> GeneralResult<EmptyResponse> { ) -> GeneralResult<StatusCode> {
db::file::get_permissions(params.file_id, claims.user_id, &state.pool) db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
.await .await
.can_write_guard()?; .can_write_guard()?;
@ -24,5 +24,5 @@ pub async fn delete(
.await .await
.handle_internal("Error deleting the file")?; .handle_internal("Error deleting the file")?;
Ok(EmptyResponse) Ok(StatusCode::NO_CONTENT)
} }

View File

@ -12,7 +12,7 @@ pub async fn modify(
State(state): State<AppState>, State(state): State<AppState>,
claims: Claims, claims: Claims,
mut multipart: Multipart, mut multipart: Multipart,
) -> GeneralResult<EmptyResponse> { ) -> GeneralResult<StatusCode> {
db::file::get_permissions(params.file_id, claims.user_id, &state.pool) db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
.await .await
.can_write_guard()?; .can_write_guard()?;
@ -52,5 +52,5 @@ pub async fn modify(
.await .await
.handle_internal("Error updating the file")?; .handle_internal("Error updating the file")?;
Ok(EmptyResponse) Ok(StatusCode::NO_CONTENT)
} }

View File

@ -36,19 +36,9 @@ async fn create_file(
pool: &Pool, pool: &Pool,
) -> anyhow::Result<Uuid> { ) -> anyhow::Result<Uuid> {
let (file_id, file) = storage.create().await?; let (file_id, file) = storage.create().await?;
let result = async {
let (hash, size) = crate::FileStorage::write_to_file(file, field).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?; db::file::insert(file_id, parent_folder, file_name, size, hash, pool).await?;
anyhow::Result::Ok(()) Ok(file_id)
}
.await;
match result {
Ok(()) => Ok(file_id),
Err(err) => {
let _ = storage.delete(file_id).await;
Err(err)
}
}
} }
async fn parse_field( async fn parse_field(

View File

@ -9,7 +9,7 @@ pub async fn delete(
State(state): State<AppState>, State(state): State<AppState>,
claims: Claims, claims: Claims,
Query(params): Query<Params>, Query(params): Query<Params>,
) -> GeneralResult<EmptyResponse> { ) -> GeneralResult<()> {
let root = db::folder::get_root(claims.user_id, &state.pool) let root = db::folder::get_root(claims.user_id, &state.pool)
.await .await
.handle_internal("Error getting the root folder")?; .handle_internal("Error getting the root folder")?;
@ -31,7 +31,5 @@ pub async fn delete(
Ok(()) Ok(())
}) })
.await .await
.handle_internal("Error deleting the fodler")?; .handle_internal("Error deleting the fodler")
Ok(EmptyResponse)
} }

View File

@ -10,7 +10,7 @@ pub async fn delete(
State(pool): State<Pool>, State(pool): State<Pool>,
claims: Claims, claims: Claims,
Query(params): Query<Params>, Query(params): Query<Params>,
) -> GeneralResult<EmptyResponse> { ) -> GeneralResult<StatusCode> {
if params.user_id != claims.user_id { if params.user_id != claims.user_id {
db::folder::get_permissions(params.folder_id, claims.user_id, &pool) db::folder::get_permissions(params.folder_id, claims.user_id, &pool)
.await .await
@ -21,5 +21,5 @@ pub async fn delete(
.await .await
.handle_internal("Error deleting the permissions")?; .handle_internal("Error deleting the permissions")?;
Ok(EmptyResponse) Ok(StatusCode::NO_CONTENT)
} }

View File

@ -11,7 +11,7 @@ pub async fn set(
claims: Claims, claims: Claims,
State(pool): State<Pool>, State(pool): State<Pool>,
Json(params): Json<Params>, Json(params): Json<Params>,
) -> GeneralResult<EmptyResponse> { ) -> GeneralResult<StatusCode> {
let root = db::folder::get_root(claims.user_id, &pool) let root = db::folder::get_root(claims.user_id, &pool)
.await .await
.handle_internal("Error getting the root folder")?; .handle_internal("Error getting the root folder")?;
@ -53,5 +53,5 @@ pub async fn set(
.await .await
.handle_internal("Error writing to the database")?; .handle_internal("Error writing to the database")?;
Ok(EmptyResponse) Ok(StatusCode::NO_CONTENT)
} }

View File

@ -1,19 +1,14 @@
use std::time::Duration;
use crate::prelude::*; use crate::prelude::*;
pub async fn delete( pub async fn delete(
State(AppState { pool, ref storage }): State<AppState>, State(AppState { pool, ref storage }): State<AppState>,
claims: Claims, claims: Claims,
) -> GeneralResult<EmptyResponse> { ) -> GeneralResult<()> {
tokio::time::sleep(Duration::from_secs(100)).await;
db::users::delete_user(claims.user_id, &pool) db::users::delete_user(claims.user_id, &pool)
.try_for_each_concurrent(5, |file_id| async move { .try_for_each_concurrent(5, |file_id| async move {
let _ = storage.delete(file_id).await; let _ = storage.delete(file_id).await;
Ok(()) Ok(())
}) })
.await .await
.handle_internal("Error deleting the user")?; .handle_internal("Error deleting the user")
Ok(EmptyResponse)
} }

View File

@ -108,7 +108,7 @@ impl FileStorage {
let mut reader = StreamReader::new(stream); let mut reader = StreamReader::new(stream);
let mut writer = BufWriter::with_capacity(BUF_CAP, file); let mut writer = BufWriter::with_capacity(BUF_CAP, file);
tokio::io::copy(&mut reader, &mut writer).await?; tokio::io::copy_buf(&mut reader, &mut writer).await?;
writer.flush().await?; writer.flush().await?;
let hash = hash.finalize().to_vec(); let hash = hash.finalize().to_vec();

View File

@ -4,16 +4,21 @@ mod endpoints;
mod errors; mod errors;
mod file_storage; mod file_storage;
mod prelude; mod prelude;
mod util;
use std::{env, net::Ipv4Addr}; use std::{env, net::Ipv4Addr};
use axum::Router; use auth::HashedBytes;
use axum::{
extract::{DefaultBodyLimit, FromRef},
routing::post,
Router,
};
use file_storage::FileStorage; use file_storage::FileStorage;
use tokio::{net::TcpListener, signal};
type Pool = sqlx::postgres::PgPool; type Pool = sqlx::postgres::PgPool;
#[derive(Clone, axum::extract::FromRef)] #[derive(Clone, FromRef)]
struct AppState { struct AppState {
pool: Pool, pool: Pool,
storage: FileStorage, storage: FileStorage,
@ -28,8 +33,8 @@ async fn create_test_users(pool: &Pool) -> anyhow::Result<()> {
if count > 0 { if count > 0 {
return Ok(()); return Ok(());
} }
let hash1 = auth::HashedBytes::hash_bytes(b"Password1").as_bytes(); let hash1 = HashedBytes::hash_bytes(b"Password1").as_bytes();
let hash2 = auth::HashedBytes::hash_bytes(b"Password2").as_bytes(); let hash2 = HashedBytes::hash_bytes(b"Password2").as_bytes();
tokio::try_join!( tokio::try_join!(
db::users::create_user("Test1", "test1@example.com", &hash1, pool), db::users::create_user("Test1", "test1@example.com", &hash1, pool),
@ -89,7 +94,7 @@ async fn main() -> anyhow::Result<()> {
let router = app(state); let router = app(state);
let addr = (Ipv4Addr::UNSPECIFIED, 3000); let addr = (Ipv4Addr::UNSPECIFIED, 3000);
let listener = tokio::net::TcpListener::bind(addr).await?; let listener = TcpListener::bind(addr).await?;
axum::serve(listener, router) axum::serve(listener, router)
.with_graceful_shutdown(shutdown_signal()) .with_graceful_shutdown(shutdown_signal())
@ -99,8 +104,6 @@ async fn main() -> anyhow::Result<()> {
} }
async fn shutdown_signal() { async fn shutdown_signal() {
use tokio::signal;
let ctrl_c = async { let ctrl_c = async {
signal::ctrl_c() signal::ctrl_c()
.await .await
@ -126,19 +129,13 @@ async fn shutdown_signal() {
} }
fn app(state: AppState) -> Router { fn app(state: AppState) -> Router {
use axum::{ use axum::{http::header, routing::get};
extract::DefaultBodyLimit,
handler::Handler as _,
http::header,
routing::{get, post},
};
use endpoints::{ use endpoints::{
file, folder, file, folder,
permissions::{self, get_top_level::get_top_level}, permissions::{self, get_top_level::get_top_level},
users, users,
}; };
use tower_http::{ use tower_http::{
timeout::TimeoutLayer,
trace::{MakeSpan, TraceLayer}, trace::{MakeSpan, TraceLayer},
ServiceBuilderExt as _, ServiceBuilderExt as _,
}; };
@ -160,24 +157,21 @@ fn app(state: AppState) -> Router {
} }
const TEN_GIBIBYTES: usize = 10 * 1024 * 1024 * 1024; const TEN_GIBIBYTES: usize = 10 * 1024 * 1024 * 1024;
let body_limit = DefaultBodyLimit::max(TEN_GIBIBYTES); let middleware = tower::ServiceBuilder::new()
.layer(DefaultBodyLimit::max(TEN_GIBIBYTES))
let timeout = TimeoutLayer::new(std::time::Duration::from_secs(10));
let common_middleware = tower::ServiceBuilder::new()
.sensitive_headers([header::AUTHORIZATION, header::COOKIE]) .sensitive_headers([header::AUTHORIZATION, header::COOKIE])
.layer(TraceLayer::new_for_http().make_span_with(SpanMaker)) .layer(TraceLayer::new_for_http().make_span_with(SpanMaker))
.compression(); .compression();
let file_router = Router::new().route( // Build route service
"/", Router::new()
.route(
"/files",
get(file::download::download) get(file::download::download)
.post(file::upload::upload.layer(body_limit.clone())) .post(file::upload::upload)
.delete(file::delete::delete.layer(timeout)) .delete(file::delete::delete)
.patch(file::modify::modify.layer(body_limit.clone())), .patch(file::modify::modify),
); )
let general_router = Router::new()
.route( .route(
"/folders", "/folders",
get(folder::list::list) get(folder::list::list)
@ -205,11 +199,6 @@ fn app(state: AppState) -> Router {
.route("/users/search", get(users::search::search)) .route("/users/search", get(users::search::search))
.route("/users/register", post(users::register::register)) .route("/users/register", post(users::register::register))
.route("/users/authorize", post(users::login::login)) .route("/users/authorize", post(users::login::login))
.layer(timeout); .layer(middleware)
Router::new()
.nest("/files", file_router)
.nest("/", general_router)
.layer(common_middleware)
.with_state(state) .with_state(state)
} }

View File

@ -2,7 +2,6 @@ pub(crate) use crate::{
auth::Claims, auth::Claims,
db::{self, permissions::PermissionExt as _}, db::{self, permissions::PermissionExt as _},
errors::{ErrorHandlingExt as _, GeneralError, GeneralResult, ItemNotFoundExt as _}, errors::{ErrorHandlingExt as _, GeneralError, GeneralResult, ItemNotFoundExt as _},
util::EmptyResponse,
AppState, Pool, AppState, Pool,
}; };
pub use axum::{ pub use axum::{

View File

@ -1,11 +0,0 @@
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()
}
}