From 1c9bd104e001b3afb79327618d3084a45a8ed0b0 Mon Sep 17 00:00:00 2001 From: StNicolay Date: Fri, 9 Aug 2024 17:02:02 +0300 Subject: [PATCH] Tweaks for the desktop client --- ...6c221e3d1d02cb9e47b4c385545298b27217.json} | 8 ++-- ...bcd8809f313026a187a2eff66fa4f7ba888a5.json | 40 +++++++++++++++++ ...7b1406552c2b7c69f4f1f02a147df5411e692.json | 2 +- ...c68d4621c6e351f9fbb7aa58629ff2d829234.json | 24 ---------- ...8e48e726eedd2b7af7e52712974b4f04a8f80.json | 17 +++++++ ...97bfff50762d36cf3742470a897f4588c802.json} | 4 +- ...576da3aecfb9fb74aabf202e83406fc8c8fff.json | 4 +- ...5c82bb8bf923736e6c797711bc3124f0693bc.json | 2 +- ...f356a0a2a47e70b6e031ea5a0442adc86725b.json | 22 --------- ...71b4dc1fcdcbc595aef4d245ee2454b0a458.json} | 4 +- Cargo.lock | 45 +++++++++++++------ Cargo.toml | 2 +- sql/create_folder.sql | 4 +- sql/get_all_permissions_for_folder.sql | 2 +- sql/get_top_level_folder.sql | 2 +- sql/search_for_user.sql | 3 +- src/db/file.rs | 7 +-- src/db/folder.rs | 22 +++++++-- src/db/permissions.rs | 25 +++++++---- src/db/users.rs | 4 +- src/endpoints/file/delete.rs | 8 +--- src/endpoints/folder/delete.rs | 2 +- src/endpoints/folder/get_structure.rs | 9 ++-- src/endpoints/permissions/delete.rs | 2 +- src/endpoints/permissions/get.rs | 8 ++-- src/endpoints/permissions/get_top_level.rs | 10 +++-- src/endpoints/permissions/set.rs | 11 ++++- src/endpoints/users/get.rs | 6 +-- src/endpoints/users/put.rs | 4 +- src/endpoints/users/register.rs | 2 +- src/errors.rs | 21 +++------ src/main.rs | 30 ++++++++++++- src/prelude.rs | 5 +-- 33 files changed, 217 insertions(+), 144 deletions(-) rename .sqlx/{query-39b78c7f3266bea5e3e44aa372574319cb74dea6b3d0bc16d25e29ca28803317.json => query-003349bc951a935fdfb285f99a726c221e3d1d02cb9e47b4c385545298b27217.json} (62%) create mode 100644 .sqlx/query-1c5dda0e613ee57819d4c9534f3bcd8809f313026a187a2eff66fa4f7ba888a5.json delete mode 100644 .sqlx/query-3dd4a65d3106d742c2221c0589ac68d4621c6e351f9fbb7aa58629ff2d829234.json create mode 100644 .sqlx/query-3faa32dd95822ae8687784817f68e48e726eedd2b7af7e52712974b4f04a8f80.json rename .sqlx/{query-8d8bf29e632723ba48a19ea52a8466424f7628e84df17178bf26eaca1cc99aca.json => query-e0d415b13ccf7aa865558395eb6997bfff50762d36cf3742470a897f4588c802.json} (87%) delete mode 100644 .sqlx/query-f9e36f45f25dd2439a7a0b16b6df356a0a2a47e70b6e031ea5a0442adc86725b.json rename .sqlx/{query-879bdad048c3151e54ee70a2c316d90dcc1a0f50a1df2c7681917a2890d082cf.json => query-fb94ebf44aff9c5c56cc43ef47f571b4dc1fcdcbc595aef4d245ee2454b0a458.json} (72%) diff --git a/.sqlx/query-39b78c7f3266bea5e3e44aa372574319cb74dea6b3d0bc16d25e29ca28803317.json b/.sqlx/query-003349bc951a935fdfb285f99a726c221e3d1d02cb9e47b4c385545298b27217.json similarity index 62% rename from .sqlx/query-39b78c7f3266bea5e3e44aa372574319cb74dea6b3d0bc16d25e29ca28803317.json rename to .sqlx/query-003349bc951a935fdfb285f99a726c221e3d1d02cb9e47b4c385545298b27217.json index 3c788cc..45d9239 100644 --- a/.sqlx/query-39b78c7f3266bea5e3e44aa372574319cb74dea6b3d0bc16d25e29ca28803317.json +++ b/.sqlx/query-003349bc951a935fdfb285f99a726c221e3d1d02cb9e47b4c385545298b27217.json @@ -1,12 +1,12 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n username,\n permission_type as \"permission_type: PermissionRaw\"\nFROM\n permissions\n INNER JOIN users ON permissions.user_id = users.user_id\nWHERE\n folder_id = $1", + "query": "SELECT\n users.user_id,\n permission_type as \"permission_type: PermissionRaw\"\nFROM\n permissions\n INNER JOIN users ON permissions.user_id = users.user_id\nWHERE\n folder_id = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "username", - "type_info": "Varchar" + "name": "user_id", + "type_info": "Int4" }, { "ordinal": 1, @@ -35,5 +35,5 @@ false ] }, - "hash": "39b78c7f3266bea5e3e44aa372574319cb74dea6b3d0bc16d25e29ca28803317" + "hash": "003349bc951a935fdfb285f99a726c221e3d1d02cb9e47b4c385545298b27217" } diff --git a/.sqlx/query-1c5dda0e613ee57819d4c9534f3bcd8809f313026a187a2eff66fa4f7ba888a5.json b/.sqlx/query-1c5dda0e613ee57819d4c9534f3bcd8809f313026a187a2eff66fa4f7ba888a5.json new file mode 100644 index 0000000..27c567b --- /dev/null +++ b/.sqlx/query-1c5dda0e613ee57819d4c9534f3bcd8809f313026a187a2eff66fa4f7ba888a5.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH\n permitted as (\n SELECT\n folder_id\n FROM\n permissions\n WHERE\n user_id = $1\n )\nSELECT\n folder_id, owner_id, folder_name, created_at\nFROM\n folders\nWHERE\n folder_id IN (\n SELECT\n folder_id\n FROM\n permitted\n )\n AND parent_folder_id NOT IN (\n SELECT\n folder_id\n FROM\n permitted\n )", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "folder_id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "owner_id", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "folder_name", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "1c5dda0e613ee57819d4c9534f3bcd8809f313026a187a2eff66fa4f7ba888a5" +} diff --git a/.sqlx/query-3028a7c8ec616933e490ed267967b1406552c2b7c69f4f1f02a147df5411e692.json b/.sqlx/query-3028a7c8ec616933e490ed267967b1406552c2b7c69f4f1f02a147df5411e692.json index 9bea034..4e843a8 100644 --- a/.sqlx/query-3028a7c8ec616933e490ed267967b1406552c2b7c69f4f1f02a147df5411e692.json +++ b/.sqlx/query-3028a7c8ec616933e490ed267967b1406552c2b7c69f4f1f02a147df5411e692.json @@ -21,7 +21,7 @@ { "ordinal": 3, "name": "created_at", - "type_info": "Timestamp" + "type_info": "Timestamptz" } ], "parameters": { diff --git a/.sqlx/query-3dd4a65d3106d742c2221c0589ac68d4621c6e351f9fbb7aa58629ff2d829234.json b/.sqlx/query-3dd4a65d3106d742c2221c0589ac68d4621c6e351f9fbb7aa58629ff2d829234.json deleted file mode 100644 index 4f66675..0000000 --- a/.sqlx/query-3dd4a65d3106d742c2221c0589ac68d4621c6e351f9fbb7aa58629ff2d829234.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO folders(parent_folder_id, owner_id, folder_name) VALUES ($1, $2, $3) RETURNING folder_id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "folder_id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Int4", - "Varchar" - ] - }, - "nullable": [ - false - ] - }, - "hash": "3dd4a65d3106d742c2221c0589ac68d4621c6e351f9fbb7aa58629ff2d829234" -} diff --git a/.sqlx/query-3faa32dd95822ae8687784817f68e48e726eedd2b7af7e52712974b4f04a8f80.json b/.sqlx/query-3faa32dd95822ae8687784817f68e48e726eedd2b7af7e52712974b4f04a8f80.json new file mode 100644 index 0000000..94f3341 --- /dev/null +++ b/.sqlx/query-3faa32dd95822ae8687784817f68e48e726eedd2b7af7e52712974b4f04a8f80.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO folders(parent_folder_id, owner_id, folder_name, folder_id) VALUES ($1, $2, $3, $4)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Int4", + "Varchar", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "3faa32dd95822ae8687784817f68e48e726eedd2b7af7e52712974b4f04a8f80" +} diff --git a/.sqlx/query-8d8bf29e632723ba48a19ea52a8466424f7628e84df17178bf26eaca1cc99aca.json b/.sqlx/query-e0d415b13ccf7aa865558395eb6997bfff50762d36cf3742470a897f4588c802.json similarity index 87% rename from .sqlx/query-8d8bf29e632723ba48a19ea52a8466424f7628e84df17178bf26eaca1cc99aca.json rename to .sqlx/query-e0d415b13ccf7aa865558395eb6997bfff50762d36cf3742470a897f4588c802.json index 6fd00b7..3aad444 100644 --- a/.sqlx/query-8d8bf29e632723ba48a19ea52a8466424f7628e84df17178bf26eaca1cc99aca.json +++ b/.sqlx/query-e0d415b13ccf7aa865558395eb6997bfff50762d36cf3742470a897f4588c802.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n user_id, username, email, \n GREATEST (\n similarity (email, $1),\n similarity (username, $1)\n ) as \"similarity!\"\nFROM\n users\nORDER BY\n \"similarity!\" DESC", + "query": "SELECT\n user_id, username, email, \n GREATEST (\n similarity (email, $1),\n similarity (username, $1)\n ) as \"similarity!\"\nFROM\n users\nORDER BY\n \"similarity!\" DESC\nLIMIT 20", "describe": { "columns": [ { @@ -36,5 +36,5 @@ null ] }, - "hash": "8d8bf29e632723ba48a19ea52a8466424f7628e84df17178bf26eaca1cc99aca" + "hash": "e0d415b13ccf7aa865558395eb6997bfff50762d36cf3742470a897f4588c802" } diff --git a/.sqlx/query-e125c9f06cb89c6ddd2653ed45c576da3aecfb9fb74aabf202e83406fc8c8fff.json b/.sqlx/query-e125c9f06cb89c6ddd2653ed45c576da3aecfb9fb74aabf202e83406fc8c8fff.json index f558664..71e5a4a 100644 --- a/.sqlx/query-e125c9f06cb89c6ddd2653ed45c576da3aecfb9fb74aabf202e83406fc8c8fff.json +++ b/.sqlx/query-e125c9f06cb89c6ddd2653ed45c576da3aecfb9fb74aabf202e83406fc8c8fff.json @@ -26,12 +26,12 @@ { "ordinal": 4, "name": "created_at", - "type_info": "Timestamp" + "type_info": "Timestamptz" }, { "ordinal": 5, "name": "updated_at", - "type_info": "Timestamp" + "type_info": "Timestamptz" } ], "parameters": { diff --git a/.sqlx/query-ef707c0f6d2ef0d66e71929167b5c82bb8bf923736e6c797711bc3124f0693bc.json b/.sqlx/query-ef707c0f6d2ef0d66e71929167b5c82bb8bf923736e6c797711bc3124f0693bc.json index ddb54f2..ab493b4 100644 --- a/.sqlx/query-ef707c0f6d2ef0d66e71929167b5c82bb8bf923736e6c797711bc3124f0693bc.json +++ b/.sqlx/query-ef707c0f6d2ef0d66e71929167b5c82bb8bf923736e6c797711bc3124f0693bc.json @@ -21,7 +21,7 @@ { "ordinal": 3, "name": "created_at", - "type_info": "Timestamp" + "type_info": "Timestamptz" } ], "parameters": { diff --git a/.sqlx/query-f9e36f45f25dd2439a7a0b16b6df356a0a2a47e70b6e031ea5a0442adc86725b.json b/.sqlx/query-f9e36f45f25dd2439a7a0b16b6df356a0a2a47e70b6e031ea5a0442adc86725b.json deleted file mode 100644 index 27dfc7c..0000000 --- a/.sqlx/query-f9e36f45f25dd2439a7a0b16b6df356a0a2a47e70b6e031ea5a0442adc86725b.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH\n permitted as (\n SELECT\n folder_id\n FROM\n permissions\n WHERE\n user_id = $1\n )\nSELECT\n folder_id\nFROM\n folders\nWHERE\n folder_id IN (\n SELECT\n folder_id\n FROM\n permitted\n )\n AND parent_folder_id NOT IN (\n SELECT\n folder_id\n FROM\n permitted\n )", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "folder_id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [ - false - ] - }, - "hash": "f9e36f45f25dd2439a7a0b16b6df356a0a2a47e70b6e031ea5a0442adc86725b" -} diff --git a/.sqlx/query-879bdad048c3151e54ee70a2c316d90dcc1a0f50a1df2c7681917a2890d082cf.json b/.sqlx/query-fb94ebf44aff9c5c56cc43ef47f571b4dc1fcdcbc595aef4d245ee2454b0a458.json similarity index 72% rename from .sqlx/query-879bdad048c3151e54ee70a2c316d90dcc1a0f50a1df2c7681917a2890d082cf.json rename to .sqlx/query-fb94ebf44aff9c5c56cc43ef47f571b4dc1fcdcbc595aef4d245ee2454b0a458.json index b1be32f..b44fe1a 100644 --- a/.sqlx/query-879bdad048c3151e54ee70a2c316d90dcc1a0f50a1df2c7681917a2890d082cf.json +++ b/.sqlx/query-fb94ebf44aff9c5c56cc43ef47f571b4dc1fcdcbc595aef4d245ee2454b0a458.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO users(username, email, hashed_password) VALUES ($1, $2, $3) RETURNING user_id", + "query": "INSERT INTO users(username, email, hashed_password) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING RETURNING user_id", "describe": { "columns": [ { @@ -20,5 +20,5 @@ false ] }, - "hash": "879bdad048c3151e54ee70a2c316d90dcc1a0f50a1df2c7681917a2890d082cf" + "hash": "fb94ebf44aff9c5c56cc43ef47f571b4dc1fcdcbc595aef4d245ee2454b0a458" } diff --git a/Cargo.lock b/Cargo.lock index deb4d1c..da371bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,9 +321,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" dependencies = [ "jobserver", "libc", @@ -920,9 +920,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-util", @@ -1259,9 +1259,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -1731,18 +1731,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", @@ -1814,6 +1814,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -2156,15 +2165,15 @@ checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "tempfile" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2255,6 +2264,7 @@ dependencies = [ "mio", "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -2653,6 +2663,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 0fcb12c..1e83826 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ sqlx = { version = "0.8", features = [ "uuid", ] } subtle = "2" -tokio = { version = "1", features = ["parking_lot", "rt-multi-thread"] } +tokio = { version = "1", features = ["parking_lot", "rt-multi-thread", "signal"] } tokio-util = { version = "0.7" } tower = { version = "0.4" } tower-http = { version = "0.5", features = [ diff --git a/sql/create_folder.sql b/sql/create_folder.sql index 086e43d..6c759b4 100644 --- a/sql/create_folder.sql +++ b/sql/create_folder.sql @@ -1,3 +1 @@ -INSERT INTO folders(parent_folder_id, owner_id, folder_name) -SELECT $1, owner_id, $2 FROM folders WHERE parent_folder_id = $1 -RETURNING folder_id \ No newline at end of file +INSERT INTO folders(parent_folder_id, owner_id, folder_name, folder_id) VALUES ($1, $2, $3, $4) \ No newline at end of file diff --git a/sql/get_all_permissions_for_folder.sql b/sql/get_all_permissions_for_folder.sql index 1dd1345..007b2b8 100644 --- a/sql/get_all_permissions_for_folder.sql +++ b/sql/get_all_permissions_for_folder.sql @@ -1,5 +1,5 @@ SELECT - username, + users.user_id, permission_type as "permission_type: PermissionRaw" FROM permissions diff --git a/sql/get_top_level_folder.sql b/sql/get_top_level_folder.sql index c051601..fe3e714 100644 --- a/sql/get_top_level_folder.sql +++ b/sql/get_top_level_folder.sql @@ -8,7 +8,7 @@ WITH user_id = $1 ) SELECT - folder_id + folder_id, owner_id, folder_name, created_at FROM folders WHERE diff --git a/sql/search_for_user.sql b/sql/search_for_user.sql index 0e3167e..88ee491 100644 --- a/sql/search_for_user.sql +++ b/sql/search_for_user.sql @@ -7,4 +7,5 @@ SELECT FROM users ORDER BY - "similarity!" DESC \ No newline at end of file + "similarity!" DESC +LIMIT 20 \ No newline at end of file diff --git a/src/db/file.rs b/src/db/file.rs index e612692..4204684 100644 --- a/src/db/file.rs +++ b/src/db/file.rs @@ -67,10 +67,11 @@ pub async fn get_permissions( } pub async fn get_name(file_id: Uuid, pool: &Pool) -> sqlx::Result> { - let record = sqlx::query!("SELECT file_name FROM files WHERE file_id = $1", file_id) + let name = sqlx::query!("SELECT file_name FROM files WHERE file_id = $1", file_id) .fetch_optional(pool) - .await?; - Ok(record.map(|record| record.file_name)) + .await? + .map(|record| record.file_name); + Ok(name) } pub async fn delete(file_id: Uuid, pool: &Pool) -> sqlx::Result { diff --git a/src/db/folder.rs b/src/db/folder.rs index 138e633..1ee27de 100644 --- a/src/db/folder.rs +++ b/src/db/folder.rs @@ -89,10 +89,24 @@ pub async fn name_exists(parent_folder_id: Uuid, name: &str, pool: &Pool) -> sql /// Creates a folder in the database. Do not use this function to create the ROOT folder pub async fn insert(parent_folder_id: Uuid, folder_name: &str, pool: &Pool) -> sqlx::Result { - sqlx::query_file!("sql/create_folder.sql", parent_folder_id, folder_name) - .fetch_one(pool) - .await - .map(|record| record.folder_id) + let folder_id = Uuid::now_v7(); + let owner_id = get_by_id(parent_folder_id, pool) + .await? + .ok_or(sqlx::Error::RowNotFound)? + .owner_id; + let result = sqlx::query_file!( + "sql/create_folder.sql", + parent_folder_id, + owner_id, + folder_name, + folder_id + ) + .execute(pool) + .await?; + if result.rows_affected() == 0 { + return Err(sqlx::Error::RowNotFound); + } + Ok(folder_id) } pub fn delete(folder_id: Uuid, pool: &Pool) -> impl Stream> + '_ { diff --git a/src/db/permissions.rs b/src/db/permissions.rs index 65836fc..9347a92 100644 --- a/src/db/permissions.rs +++ b/src/db/permissions.rs @@ -1,8 +1,11 @@ use std::{borrow::Cow, collections::HashMap}; +use db::folder::FolderWithoutParentId; + use crate::prelude::*; #[derive(sqlx::Type, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] #[sqlx(type_name = "permission")] #[sqlx(rename_all = "lowercase")] pub enum PermissionRaw { @@ -120,10 +123,10 @@ pub async fn insert( pub async fn get_all_for_folder( folder_id: Uuid, pool: &Pool, -) -> sqlx::Result> { +) -> sqlx::Result> { sqlx::query_file!("sql/get_all_permissions_for_folder.sql", folder_id) .fetch(pool) - .map_ok(|record| (record.username, record.permission_type)) + .map_ok(|record| (record.user_id, record.permission_type)) .try_collect() .await } @@ -135,10 +138,16 @@ pub async fn delete_for_folder(folder_id: Uuid, user_id: i32, pool: &Pool) -> sq .map(|_| ()) } -pub async fn get_top_level_permitted_folders(user_id: i32, pool: &Pool) -> sqlx::Result> { - sqlx::query_file!("sql/get_top_level_folder.sql", user_id) - .fetch(pool) - .map_ok(|record| record.folder_id) - .try_collect() - .await +pub async fn get_top_level_permitted_folders( + user_id: i32, + pool: &Pool, +) -> sqlx::Result> { + sqlx::query_file_as!( + FolderWithoutParentId, + "sql/get_top_level_folder.sql", + user_id + ) + .fetch(pool) + .try_collect() + .await } diff --git a/src/db/users.rs b/src/db/users.rs index a422f5c..088446b 100644 --- a/src/db/users.rs +++ b/src/db/users.rs @@ -8,7 +8,7 @@ pub async fn create_user( pool: &Pool, ) -> sqlx::Result> { let Some(record) = sqlx::query!( - "INSERT INTO users(username, email, hashed_password) VALUES ($1, $2, $3) RETURNING user_id", + "INSERT INTO users(username, email, hashed_password) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING RETURNING user_id", user_name, user_email, hashed_password @@ -80,7 +80,7 @@ pub async fn get(user_id: i32, pool: &Pool) -> sqlx::Result> { .await } -/// Gets the hashed password field by either the email or th username +/// Gets the hashed password field by either the email or the username pub async fn get_hash(search_string: &str, pool: &Pool) -> sqlx::Result)>> { let record = sqlx::query!( "SELECT user_id, hashed_password FROM users WHERE username = $1 OR email = $1", diff --git a/src/endpoints/file/delete.rs b/src/endpoints/file/delete.rs index 434857e..f47a5ac 100644 --- a/src/endpoints/file/delete.rs +++ b/src/endpoints/file/delete.rs @@ -14,15 +14,9 @@ pub async fn delete( .await .can_write_guard()?; - let deleted = db::file::delete(params.file_id, &state.pool) + db::file::delete(params.file_id, &state.pool) .await .handle_internal("Error deleting the file")?; - if !deleted { - return Err(GeneralError::message( - StatusCode::NOT_FOUND, - "Item not found", - )); // Will not happen most of the time due to can write guard - } state .storage diff --git a/src/endpoints/folder/delete.rs b/src/endpoints/folder/delete.rs index 30e9c4d..1c6ba90 100644 --- a/src/endpoints/folder/delete.rs +++ b/src/endpoints/folder/delete.rs @@ -8,7 +8,7 @@ pub struct Params { pub async fn delete( State(state): State, claims: Claims, - Json(params): Json, + Query(params): Query, ) -> GeneralResult<()> { let root = db::folder::get_root(claims.user_id, &state.pool) .await diff --git a/src/endpoints/folder/get_structure.rs b/src/endpoints/folder/get_structure.rs index da67ab2..472981f 100644 --- a/src/endpoints/folder/get_structure.rs +++ b/src/endpoints/folder/get_structure.rs @@ -1,3 +1,4 @@ +use db::{file::FileWithoutParentId, folder::FolderWithoutParentId}; use tokio::try_join; use super::list::Params; @@ -6,13 +7,13 @@ use crate::prelude::*; #[derive(Serialize, Debug)] pub struct FolderStructure { #[serde(flatten)] - folder_base: db::folder::FolderWithoutParentId, + folder_base: FolderWithoutParentId, folders: Vec, - files: Vec, + files: Vec, } -impl From for FolderStructure { - fn from(value: db::folder::FolderWithoutParentId) -> Self { +impl From for FolderStructure { + fn from(value: FolderWithoutParentId) -> Self { FolderStructure { folder_base: value, folders: Vec::new(), diff --git a/src/endpoints/permissions/delete.rs b/src/endpoints/permissions/delete.rs index 177f6ad..3f326de 100644 --- a/src/endpoints/permissions/delete.rs +++ b/src/endpoints/permissions/delete.rs @@ -9,7 +9,7 @@ pub struct Params { pub async fn delete( State(pool): State, claims: Claims, - Json(params): Json, + Query(params): Query, ) -> GeneralResult { if params.user_id != claims.user_id { db::folder::get_permissions(params.folder_id, claims.user_id, &pool) diff --git a/src/endpoints/permissions/get.rs b/src/endpoints/permissions/get.rs index 98983b2..5cd590a 100644 --- a/src/endpoints/permissions/get.rs +++ b/src/endpoints/permissions/get.rs @@ -13,13 +13,13 @@ pub async fn get( State(pool): State, Query(params): Query, claims: Claims, -) -> GeneralResult>> { +) -> GeneralResult>> { db::folder::get_permissions(params.folder_id, claims.user_id, &pool) .await .can_manage_guard()?; - let permissions = db::permissions::get_all_for_folder(params.folder_id, &pool) + db::permissions::get_all_for_folder(params.folder_id, &pool) .await - .handle_internal("Error getting permissions")?; - Ok(Json(permissions)) + .handle_internal("Error getting permissions") + .map(Json) } diff --git a/src/endpoints/permissions/get_top_level.rs b/src/endpoints/permissions/get_top_level.rs index 55ebba6..0f96c3e 100644 --- a/src/endpoints/permissions/get_top_level.rs +++ b/src/endpoints/permissions/get_top_level.rs @@ -1,11 +1,13 @@ +use db::folder::FolderWithoutParentId; + use crate::prelude::*; pub async fn get_top_level( State(pool): State, claims: Claims, -) -> GeneralResult>> { - let folders = db::permissions::get_top_level_permitted_folders(claims.user_id, &pool) +) -> GeneralResult>> { + db::permissions::get_top_level_permitted_folders(claims.user_id, &pool) .await - .handle_internal("Error reading from the database")?; - Ok(Json(folders)) + .handle_internal("Error reading from the database") + .map(Json) } diff --git a/src/endpoints/permissions/set.rs b/src/endpoints/permissions/set.rs index 768618c..c850699 100644 --- a/src/endpoints/permissions/set.rs +++ b/src/endpoints/permissions/set.rs @@ -18,7 +18,7 @@ pub async fn set( if params.folder_id == root { return Err(GeneralError::message( StatusCode::BAD_REQUEST, - "Cannot delete the root folder", + "Cannot set permissions for the root folder", )); } @@ -26,6 +26,13 @@ pub async fn set( .await .can_manage_guard()?; + if params.user_id == claims.user_id { + return Err(GeneralError::message( + StatusCode::BAD_REQUEST, + "Cannot set your own permissions", + )); + } + let folder_info = db::folder::get_by_id(params.folder_id, &pool) .await .handle_internal("Error getting folder info")? @@ -33,7 +40,7 @@ pub async fn set( if folder_info.owner_id == params.user_id { return Err(GeneralError::message( StatusCode::BAD_REQUEST, - "Cannot set permissions of the folder owner", + "Cannot set permissions of the folder's owner", )); } diff --git a/src/endpoints/users/get.rs b/src/endpoints/users/get.rs index 96f040c..95fea3e 100644 --- a/src/endpoints/users/get.rs +++ b/src/endpoints/users/get.rs @@ -8,11 +8,11 @@ pub struct Params { type Response = GeneralResult>; pub async fn get(State(pool): State, Query(params): Query) -> Response { - let info = db::users::get(params.user_id, &pool) + db::users::get(params.user_id, &pool) .await .handle_internal("Error getting the user")? - .handle(StatusCode::NOT_FOUND, "User not found")?; - Ok(Json(info)) + .handle(StatusCode::NOT_FOUND, "User not found") + .map(Json) } pub async fn current(state: State, claims: Claims) -> Response { diff --git a/src/endpoints/users/put.rs b/src/endpoints/users/put.rs index 763317f..fa41ddb 100644 --- a/src/endpoints/users/put.rs +++ b/src/endpoints/users/put.rs @@ -4,7 +4,7 @@ use crate::prelude::*; #[derive(Deserialize, Debug, Validate)] pub struct Params { - #[validate(email)] + #[validate(length(min = 3, max = 10))] username: String, #[validate(email)] email: String, @@ -15,7 +15,7 @@ pub async fn put( claims: Claims, Json(params): Json, ) -> GeneralResult> { - params.validate().handle_validation()?; + params.validate()?; db::users::update(claims.user_id, ¶ms.username, ¶ms.email, &pool) .await .handle_internal("Error updating the user") diff --git a/src/endpoints/users/register.rs b/src/endpoints/users/register.rs index 81447be..5a4343d 100644 --- a/src/endpoints/users/register.rs +++ b/src/endpoints/users/register.rs @@ -48,7 +48,7 @@ pub async fn register( State(pool): State, Form(params): Form, ) -> GeneralResult> { - params.validate().handle_validation()?; + params.validate()?; let password = HashedBytes::hash_bytes(params.password.as_bytes()).as_bytes(); let id = db::users::create_user(¶ms.username, ¶ms.email, &password, &pool) diff --git a/src/errors.rs b/src/errors.rs index b82bb46..15e9ff2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,6 +41,12 @@ impl IntoResponse for GeneralError { } } +impl From for GeneralError { + fn from(value: validator::ValidationErrors) -> Self { + GeneralError::message(StatusCode::BAD_REQUEST, value.to_string()) + } +} + pub type GeneralResult = Result; pub trait ErrorHandlingExt @@ -92,19 +98,6 @@ pub trait ItemNotFoundExt { impl ItemNotFoundExt for Option { fn item_not_found(self) -> GeneralResult { - self.ok_or(GeneralError::const_message( - StatusCode::NOT_FOUND, - "Item not found", - )) - } -} - -pub trait ValidationExt { - fn handle_validation(self) -> GeneralResult; -} - -impl ValidationExt for Result { - fn handle_validation(self) -> GeneralResult { - self.map_err(|err| GeneralError::message(StatusCode::BAD_REQUEST, err.to_string())) + self.handle(StatusCode::NOT_FOUND, "Item not found") } } diff --git a/src/main.rs b/src/main.rs index 09c504e..43b7029 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use std::{env, net::Ipv4Addr}; use auth::HashedBytes; use axum::{extract::FromRef, routing::post, Router}; use file_storage::FileStorage; -use tokio::net::TcpListener; +use tokio::{net::TcpListener, signal}; type Pool = sqlx::postgres::PgPool; @@ -66,11 +66,37 @@ async fn main() -> anyhow::Result<()> { let addr = (Ipv4Addr::UNSPECIFIED, 3000); let listener = TcpListener::bind(addr).await?; - axum::serve(listener, router).await?; + axum::serve(listener, router) + .with_graceful_shutdown(shutdown_signal()) + .await?; Ok(()) } +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + () = ctrl_c => {}, + () = terminate => {}, + } +} + fn app(state: AppState) -> Router { use axum::{http::header, routing::get}; use endpoints::{ diff --git a/src/prelude.rs b/src/prelude.rs index 92d18f5..a97b938 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,10 +1,7 @@ pub(crate) use crate::{ auth::Claims, db::{self, permissions::PermissionExt as _}, - errors::{ - ErrorHandlingExt as _, GeneralError, GeneralResult, ItemNotFoundExt as _, - ValidationExt as _, - }, + errors::{ErrorHandlingExt as _, GeneralError, GeneralResult, ItemNotFoundExt as _}, AppState, Pool, }; pub use axum::{