diff --git a/.sqlx/query-443854b9fb234840588e0100774d3ab51c4532b14d50e5ca3578fe6311e2017a.json b/.sqlx/query-443854b9fb234840588e0100774d3ab51c4532b14d50e5ca3578fe6311e2017a.json new file mode 100644 index 0000000..ab8541e --- /dev/null +++ b/.sqlx/query-443854b9fb234840588e0100774d3ab51c4532b14d50e5ca3578fe6311e2017a.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n EXISTS (\n SELECT\n file_name as name\n FROM\n files\n WHERE\n folder_id = $1\n AND file_name = $2\n UNION\n SELECT\n folder_name as name\n FROM\n folders\n WHERE\n parent_folder_id = $1\n AND folder_name = $2\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "443854b9fb234840588e0100774d3ab51c4532b14d50e5ca3578fe6311e2017a" +} diff --git a/.sqlx/query-cd3591c61f3cc036158d8d55ec22a04adaf62ec4b05ba73da9253128b7bbb5b1.json b/.sqlx/query-cd3591c61f3cc036158d8d55ec22a04adaf62ec4b05ba73da9253128b7bbb5b1.json deleted file mode 100644 index 8fc6bb5..0000000 --- a/.sqlx/query-cd3591c61f3cc036158d8d55ec22a04adaf62ec4b05ba73da9253128b7bbb5b1.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT EXISTS(SELECT folder_id FROM folders WHERE parent_folder_id = $1 AND folder_name = $2)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "exists", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "cd3591c61f3cc036158d8d55ec22a04adaf62ec4b05ba73da9253128b7bbb5b1" -} diff --git a/Cargo.lock b/Cargo.lock index baea03d..6200663 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -321,9 +321,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" [[package]] name = "cc" @@ -1482,12 +1482,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.19" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" +checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" dependencies = [ - "zerocopy", - "zerocopy-derive", + "zerocopy 0.6.6", ] [[package]] @@ -3115,14 +3114,34 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "zerocopy" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" +dependencies = [ + "byteorder", + "zerocopy-derive 0.6.6", +] + [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 958cd6a..160a9a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,4 +57,4 @@ tracing-subscriber = { version = "0.3", features = [ ] } utoipa = { version = "4", features = ["axum_extras", "uuid", "chrono"] } utoipauto = "0.1" -uuid = { version = "1", features = ["serde", "v4"] } +uuid = { version = "1", features = ["serde", "v7"] } diff --git a/compose.yaml b/compose.yaml index 956da66..eccc763 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,6 +1,6 @@ services: db: - image: postgres:alpine + image: ghcr.io/fboulnois/pg_uuidv7:1.5.0 environment: - POSTGRES_USER=tester - POSTGRES_PASSWORD=testing123! diff --git a/migrations/0001_initial.down.sql b/migrations/0001_initial.down.sql index 85d8222..f5b31f1 100644 --- a/migrations/0001_initial.down.sql +++ b/migrations/0001_initial.down.sql @@ -1,3 +1,7 @@ +DROP EXTENSION IF EXISTS pg_trgm; + +DROP EXTENSION IF EXISTS pg_uuidv7; + DROP TABLE permissions; DROP TABLE files; @@ -6,6 +10,4 @@ DROP TABLE folders; DROP TABLE users; -DROP TYPE permission; - -DROP EXTENSION pg_trgm; \ No newline at end of file +DROP TYPE permission; \ No newline at end of file diff --git a/migrations/0001_initial.up.sql b/migrations/0001_initial.up.sql index 9bc2e2d..3b5bb92 100644 --- a/migrations/0001_initial.up.sql +++ b/migrations/0001_initial.up.sql @@ -1,5 +1,7 @@ CREATE EXTENSION IF NOT EXISTS pg_trgm; +CREATE EXTENSION IF NOT EXISTS pg_uuidv7; + CREATE TABLE users ( user_id SERIAL PRIMARY KEY, @@ -9,7 +11,7 @@ CREATE TABLE CREATE TABLE folders ( - folder_id UUID PRIMARY KEY DEFAULT gen_random_uuid (), + folder_id UUID PRIMARY KEY DEFAULT uuid_generate_v7 (), parent_folder_id UUID REFERENCES folders (folder_id) ON DELETE CASCADE DEFAULT null, owner_id INT REFERENCES users (user_id) ON DELETE CASCADE NOT NULL, folder_name VARCHAR(255) NOT NULL, @@ -18,7 +20,7 @@ CREATE TABLE CREATE TABLE files ( - file_id UUID PRIMARY KEY DEFAULT gen_random_uuid (), + file_id UUID PRIMARY KEY DEFAULT uuid_generate_v7 (), folder_id UUID REFERENCES folders (folder_id) ON DELETE CASCADE NOT NULL, file_name VARCHAR(255) NOT NULL, file_size BIGINT NOT NULL, diff --git a/src/endpoints/folder/create.rs b/src/endpoints/folder/create.rs index 9f7719f..f9c0a42 100644 --- a/src/endpoints/folder/create.rs +++ b/src/endpoints/folder/create.rs @@ -7,16 +7,16 @@ pub struct Params { } pub async fn create( - State(state): State, + State(pool): State, claims: Claims, Json(params): Json, ) -> Result, StatusCode> { - db::folder::get_permissions(params.parent_folder_id, claims.user_id, &state.pool) + db::folder::get_permissions(params.parent_folder_id, claims.user_id, &pool) .await .handle_internal()? .can_write_guard()?; - let exists = db::folder::name_exists(params.parent_folder_id, ¶ms.folder_name, &state.pool) + let exists = db::folder::name_exists(params.parent_folder_id, ¶ms.folder_name, &pool) .await .handle_internal()?; if exists { @@ -27,7 +27,7 @@ pub async fn create( params.parent_folder_id, claims.user_id, ¶ms.folder_name, - &state.pool, + &pool, ) .await .handle_internal()?; diff --git a/src/endpoints/folder/list.rs b/src/endpoints/folder/list.rs index a6e0953..2f092d9 100644 --- a/src/endpoints/folder/list.rs +++ b/src/endpoints/folder/list.rs @@ -16,17 +16,17 @@ pub struct Response { pub async fn list( Query(params): Query, - State(state): State, + State(pool): State, claims: Claims, ) -> Result, StatusCode> { - let folder_id = db::folder::get_by_id(params.folder_id, claims.user_id, &state.pool) + let folder_id = db::folder::get_by_id(params.folder_id, claims.user_id, &pool) .await .handle_internal()? .ok_or(StatusCode::NOT_FOUND)?; let (files, folders) = try_join!( - db::file::get_files(folder_id, &state.pool), - db::folder::get_folders(folder_id, &state.pool) + db::file::get_files(folder_id, &pool), + db::folder::get_folders(folder_id, &pool) ) .handle_internal()?; diff --git a/src/endpoints/permissions/delete.rs b/src/endpoints/permissions/delete.rs index 567cf74..52acafd 100644 --- a/src/endpoints/permissions/delete.rs +++ b/src/endpoints/permissions/delete.rs @@ -7,18 +7,18 @@ pub struct Params { } pub async fn delete( + State(pool): State, claims: Claims, - State(state): State, Json(params): Json, ) -> Result { if params.user_id != claims.user_id { - db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool) + db::folder::get_permissions(params.folder_id, claims.user_id, &pool) .await .handle_internal()? .can_manage_guard()?; } - db::permissions::delete_for_folder(params.folder_id, params.user_id, &state.pool) + db::permissions::delete_for_folder(params.folder_id, params.user_id, &pool) .await .handle_internal()?; diff --git a/src/endpoints/permissions/get.rs b/src/endpoints/permissions/get.rs index 667dd39..eec875a 100644 --- a/src/endpoints/permissions/get.rs +++ b/src/endpoints/permissions/get.rs @@ -10,16 +10,16 @@ pub struct Params { } pub async fn get( + State(pool): State, Query(params): Query, claims: Claims, - State(state): State, ) -> Result>, StatusCode> { - db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool) + db::folder::get_permissions(params.folder_id, claims.user_id, &pool) .await .handle_internal()? .can_manage_guard()?; - let permissions = db::permissions::get_all_for_folder(params.folder_id, &state.pool) + let permissions = db::permissions::get_all_for_folder(params.folder_id, &pool) .await .handle_internal()?; Ok(Json(permissions)) diff --git a/src/endpoints/permissions/get_top_level.rs b/src/endpoints/permissions/get_top_level.rs index 3125a05..95d7d0e 100644 --- a/src/endpoints/permissions/get_top_level.rs +++ b/src/endpoints/permissions/get_top_level.rs @@ -1,10 +1,10 @@ use crate::prelude::*; pub async fn get_top_level( - State(state): State, + State(pool): State, claims: Claims, ) -> Result>, StatusCode> { - let folders = db::permissions::get_top_level_permitted_folders(claims.user_id, &state.pool) + let folders = db::permissions::get_top_level_permitted_folders(claims.user_id, &pool) .await .handle_internal()?; Ok(Json(folders)) diff --git a/src/endpoints/permissions/set.rs b/src/endpoints/permissions/set.rs index 23d8a41..5634381 100644 --- a/src/endpoints/permissions/set.rs +++ b/src/endpoints/permissions/set.rs @@ -11,17 +11,17 @@ pub struct Params { pub async fn set( claims: Claims, - State(state): State, + State(pool): State, Json(params): Json, ) -> Result { - let root = db::folder::get_root(claims.user_id, &state.pool) + let root = db::folder::get_root(claims.user_id, &pool) .await .handle_internal()?; if params.folder_id == root { return Err(StatusCode::BAD_REQUEST); } - db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool) + db::folder::get_permissions(params.folder_id, claims.user_id, &pool) .await .handle_internal()? .can_manage_guard()?; @@ -30,7 +30,7 @@ pub async fn set( params.user_id, params.folder_id, params.permission_type, - &state.pool, + &pool, ) .await .handle_internal()?; diff --git a/src/endpoints/users/create.rs b/src/endpoints/users/create.rs index 8b94ca3..5c2023f 100644 --- a/src/endpoints/users/create.rs +++ b/src/endpoints/users/create.rs @@ -7,10 +7,10 @@ pub struct Params { } pub async fn create( - State(state): State, + State(pool): State, Json(params): Json, ) -> Result, StatusCode> { - let id = db::users::create_user(¶ms.username, ¶ms.email, &state.pool) + let id = db::users::create_user(¶ms.username, ¶ms.email, &pool) .await .handle_internal()?; Ok(Json(id)) diff --git a/src/endpoints/users/delete.rs b/src/endpoints/users/delete.rs index 0f87088..40f7f2a 100644 --- a/src/endpoints/users/delete.rs +++ b/src/endpoints/users/delete.rs @@ -2,9 +2,11 @@ use futures::TryStreamExt; use crate::prelude::*; -pub async fn delete(State(state): State, claims: Claims) -> Result<(), StatusCode> { - let storage = &state.storage; - db::users::delete_user(claims.user_id, &state.pool) +pub async fn delete( + State(AppState { pool, ref storage }): State, + claims: Claims, +) -> Result<(), StatusCode> { + db::users::delete_user(claims.user_id, &pool) .try_for_each_concurrent(5, |file_id| async move { let _ = storage.delete(file_id).await; Ok(()) diff --git a/src/endpoints/users/get.rs b/src/endpoints/users/get.rs index e389202..203673d 100644 --- a/src/endpoints/users/get.rs +++ b/src/endpoints/users/get.rs @@ -6,10 +6,10 @@ pub struct Params { } pub async fn get( - State(state): State, + State(pool): State, Query(params): Query, ) -> Result, StatusCode> { - let info = db::users::get(params.user_id, &state.pool) + let info = db::users::get(params.user_id, &pool) .await .handle_internal()?; Ok(Json(info)) diff --git a/src/endpoints/users/put.rs b/src/endpoints/users/put.rs index 44d28b6..e62c5ec 100644 --- a/src/endpoints/users/put.rs +++ b/src/endpoints/users/put.rs @@ -7,11 +7,11 @@ pub struct Params { } pub async fn put( - State(state): State, + State(pool): State, claims: Claims, Json(params): Json, ) -> Result, StatusCode> { - let info = db::users::update(claims.user_id, ¶ms.username, ¶ms.email, &state.pool) + let info = db::users::update(claims.user_id, ¶ms.username, ¶ms.email, &pool) .await .handle_internal()?; Ok(Json(info)) diff --git a/src/endpoints/users/search.rs b/src/endpoints/users/search.rs index 701ab05..1a9a6e6 100644 --- a/src/endpoints/users/search.rs +++ b/src/endpoints/users/search.rs @@ -8,11 +8,11 @@ pub struct Params { } pub async fn search( - State(state): State, + State(pool): State, Query(params): Query, ) -> sqlx::Result>, StatusCode> { - let users = db::users::search_for_user(¶ms.search_string, &state.pool) - .take(5) + let users = db::users::search_for_user(¶ms.search_string, &pool) + .take(20) .try_collect() .await .handle_internal()?; diff --git a/src/file_storage.rs b/src/file_storage.rs index cdcd4e4..2c3e6ab 100644 --- a/src/file_storage.rs +++ b/src/file_storage.rs @@ -21,7 +21,7 @@ impl FileStorage { pub fn new() -> anyhow::Result { let var = env::var("DRIVE_STORAGE_PATH"); let path_str = match var { - Ok(ref string) => string, + Ok(ref string) => string.trim(), Err(err) => { tracing::info!( %err, @@ -55,7 +55,7 @@ impl FileStorage { pub async fn create(&self) -> anyhow::Result<(Uuid, impl tokio::io::AsyncWrite)> { let mut error = anyhow::anyhow!("Error creating a file"); for _ in 0..3 { - let file_id = Uuid::new_v4(); + let file_id = Uuid::now_v7(); match self.create_inner(file_id).await { Ok(file) => return Ok((file_id, file)), Err(err) => error = error.context(err), diff --git a/src/main.rs b/src/main.rs index 683f98e..5234a7e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ mod prelude; use std::{env, net::Ipv4Addr}; -use axum::Router; +use axum::{extract::FromRef, Router}; use file_storage::FileStorage; use tokio::net::TcpListener; @@ -19,7 +19,13 @@ struct AppState { storage: FileStorage, } -async fn create_debug_users(pool: &Pool) -> anyhow::Result<()> { +impl FromRef for Pool { + fn from_ref(input: &AppState) -> Self { + input.pool.clone() + } +} + +async fn create_test_users(pool: &Pool) -> anyhow::Result<()> { let count = sqlx::query!("SELECT count(user_id) FROM users") .fetch_one(pool) .await? @@ -49,11 +55,14 @@ async fn main() -> anyhow::Result<()> { Err(err) => anyhow::bail!("Error getting database url: {err}"), }; sqlx::migrate!().run(&pool).await?; - create_debug_users(&pool).await?; + if let Ok("1") = env::var("DEVELOPMENT").as_deref().map(str::trim_ascii) { + create_test_users(&pool).await?; + } - let storage = file_storage::FileStorage::new()?; - - let state = AppState { pool, storage }; + let state = AppState { + pool, + storage: FileStorage::new()?, + }; let router = app(state); let addr = (Ipv4Addr::UNSPECIFIED, 3000);