User endpoints

This commit is contained in:
StNicolay 2024-07-30 20:21:33 +03:00
parent 33356f34e8
commit d4c1cdb582
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
32 changed files with 551 additions and 94 deletions

View File

@ -1,6 +1,6 @@
{ {
"db_name": "PostgreSQL", "db_name": "PostgreSQL",
"query": "SELECT folder_id FROM folders WHERE folder_id = $1 AND owner_id = $2", "query": "SELECT folder_id FROM files WHERE file_id = $1",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@ -11,13 +11,12 @@
], ],
"parameters": { "parameters": {
"Left": [ "Left": [
"Uuid", "Uuid"
"Int4"
] ]
}, },
"nullable": [ "nullable": [
false false
] ]
}, },
"hash": "dc98f1a609e67b642aed635b26239328e6456e69c12dc8561c474fff3dcd14d5" "hash": "09299172474d10a07387b74f4d714bf389b5422334bd1aa2a0e6f2d63ebdd623"
} }

View File

@ -0,0 +1,39 @@
{
"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",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "username",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "permission_type: PermissionRaw",
"type_info": {
"Custom": {
"name": "permission",
"kind": {
"Enum": [
"read",
"write",
"manage"
]
}
}
}
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
false,
false
]
},
"hash": "39b78c7f3266bea5e3e44aa372574319cb74dea6b3d0bc16d25e29ca28803317"
}

View File

@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE files SET (sha512, file_size, updated_at) = ($2, $3, NOW()) WHERE file_id = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Bytea",
"Int8"
]
},
"nullable": []
},
"hash": "4c7c701a22c49eebb4ba31a21dab15a5dbe2eaba99036932790c7c09599d4cd7"
}

View File

@ -0,0 +1,27 @@
{
"db_name": "PostgreSQL",
"query": "WITH RECURSIVE folder_hierarchy AS (\n -- Start with the given directory\n SELECT \n folder_id \n FROM \n folders \n WHERE \n folder_id = $1\n\n UNION ALL\n\n -- Recursively find all subdirectories\n SELECT \n f.folder_id\n FROM \n folders f\n INNER JOIN \n folder_hierarchy fh ON f.parent_folder_id = fh.folder_id\n)\nINSERT INTO permissions(user_id, folder_id, permission_type)\nSELECT $2::integer as user_id, fh.folder_id::UUID as folder_id, $3\nFROM folder_hierarchy fh\nON CONFLICT (user_id, folder_id) DO UPDATE\nSET permission_type = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Int4",
{
"Custom": {
"name": "permission",
"kind": {
"Enum": [
"read",
"write",
"manage"
]
}
}
}
]
},
"nullable": []
},
"hash": "595739c03acbe706107e34a3ebebec8c8f21f70ccc51b5366eff962d9af391d7"
}

View File

@ -0,0 +1,34 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n user_id, username, email\nFROM\n users\nORDER BY\n GREATEST (\n similarity (email, $1),\n similarity (username, $1)\n ) DESC",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "user_id",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "username",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "email",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "61a26b3321bb5b58a0b90e61b2cdcacfb46a03eb0c0a89839c9b3eff53cb7e56"
}

View File

@ -1,13 +1,12 @@
{ {
"db_name": "PostgreSQL", "db_name": "PostgreSQL",
"query": "INSERT INTO files(file_id, folder_id, owner_id, file_name, file_size, sha512) VALUES ($1, $2, $3, $4, $5, $6)", "query": "INSERT INTO files(file_id, folder_id, file_name, file_size, sha512) VALUES ($1, $2, $3, $4, $5)",
"describe": { "describe": {
"columns": [], "columns": [],
"parameters": { "parameters": {
"Left": [ "Left": [
"Uuid", "Uuid",
"Uuid", "Uuid",
"Int4",
"Varchar", "Varchar",
"Int8", "Int8",
"Bytea" "Bytea"
@ -15,5 +14,5 @@
}, },
"nullable": [] "nullable": []
}, },
"hash": "9a70e24a3de68f4a66718124bd3ca959bd0a992e5e0dda3baae52b8cb545ce66" "hash": "6b58c84cdc19cea97ef025211a98879bb5cc80a934490125a19c960133f6d93d"
} }

View File

@ -0,0 +1,34 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n permission_type as \"permission_type: PermissionRaw\"\nFROM\n permissions\nWHERE\n folder_id = $1\n AND user_id = $2\nUNION\nSELECT\n 'manage' as \"permission_type: PermissionRaw\"\nFROM\n folders\nWHERE\n folder_id = $1\n AND owner_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "permission_type: PermissionRaw",
"type_info": {
"Custom": {
"name": "permission",
"kind": {
"Enum": [
"read",
"write",
"manage"
]
}
}
}
}
],
"parameters": {
"Left": [
"Uuid",
"Int4"
]
},
"nullable": [
null
]
},
"hash": "87f7df91208438a35516604f57f0443e0f12db718e23acd374f6f7ace65f467d"
}

View File

@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "WITH RECURSIVE folder_hierarchy AS (\n -- Start with the given directory\n SELECT \n folder_id \n FROM \n folders \n WHERE \n folder_id = $1\n\n UNION ALL\n\n -- Recursively find all subdirectories\n SELECT \n f.folder_id\n FROM \n folders f\n INNER JOIN \n folder_hierarchy fh ON f.parent_folder_id = fh.folder_id\n)\nDELETE FROM permissions WHERE user_id = $2 AND folder_id IN (SELECT folder_id FROM folder_hierarchy)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Int4"
]
},
"nullable": []
},
"hash": "948f13b631bcc7df1919a9639443f0ed932c4cb37f2ba5bf6f000eb84b265ae2"
}

View File

@ -1,23 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT file_id FROM files WHERE file_id = $1 AND owner_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "file_id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Uuid",
"Int4"
]
},
"nullable": [
false
]
},
"hash": "9a26dab9efbbbb92b7be27792b581a0156210fdc0aadd3756f7003186f428374"
}

View File

@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "WITH\n deleted_files AS (\n DELETE FROM files USING folders\n WHERE\n files.folder_id = folders.folder_id\n AND folders.owner_id = $1 RETURNING files.file_id\n ),\n deleted_users AS (\n DELETE FROM users\n WHERE\n user_id = $1\n )\nSELECT\n *\nFROM\n deleted_files;",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "file_id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": [
false
]
},
"hash": "a54829e9cd90e55022c2f6dd413b797efaafd1c4793b60886140bfe9ea6df592"
}

View File

@ -1,6 +1,6 @@
{ {
"db_name": "PostgreSQL", "db_name": "PostgreSQL",
"query": "SELECT file_id, owner_id, file_name, file_size, sha512, created_at, updated_at FROM files WHERE folder_id = $1", "query": "SELECT file_id, file_name, file_size, encode(sha512, 'base64') as \"sha512!\", created_at, updated_at FROM files WHERE folder_id = $1",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@ -10,31 +10,26 @@
}, },
{ {
"ordinal": 1, "ordinal": 1,
"name": "owner_id",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "file_name", "name": "file_name",
"type_info": "Varchar" "type_info": "Varchar"
}, },
{ {
"ordinal": 3, "ordinal": 2,
"name": "file_size", "name": "file_size",
"type_info": "Int8" "type_info": "Int8"
}, },
{ {
"ordinal": 4, "ordinal": 3,
"name": "sha512", "name": "sha512!",
"type_info": "Bytea" "type_info": "Text"
}, },
{ {
"ordinal": 5, "ordinal": 4,
"name": "created_at", "name": "created_at",
"type_info": "Timestamp" "type_info": "Timestamp"
}, },
{ {
"ordinal": 6, "ordinal": 5,
"name": "updated_at", "name": "updated_at",
"type_info": "Timestamp" "type_info": "Timestamp"
} }
@ -48,11 +43,10 @@
false, false,
false, false,
false, false,
false, null,
false,
false, false,
false false
] ]
}, },
"hash": "5a51ab540453327bdd75f49991f402fac6b1d8fb0a760d420236e2b41d3e7fcf" "hash": "e125c9f06cb89c6ddd2653ed45c576da3aecfb9fb74aabf202e83406fc8c8fff"
} }

View File

@ -0,0 +1,22 @@
{
"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"
}

123
Cargo.lock generated
View File

@ -107,7 +107,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -220,7 +220,7 @@ dependencies = [
"heck 0.4.1", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -663,7 +663,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -1079,6 +1079,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@ -1419,7 +1420,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -1523,7 +1524,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -1581,6 +1582,30 @@ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.86"
@ -1612,6 +1637,8 @@ dependencies = [
"tower-http", "tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"utoipa",
"utoipauto",
"uuid", "uuid",
] ]
@ -2060,7 +2087,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -2274,7 +2301,7 @@ dependencies = [
"quote", "quote",
"sqlx-core", "sqlx-core",
"sqlx-macros-core", "sqlx-macros-core",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -2297,7 +2324,7 @@ dependencies = [
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres", "sqlx-postgres",
"sqlx-sqlite", "sqlx-sqlite",
"syn", "syn 2.0.72",
"tempfile", "tempfile",
"tokio", "tokio",
"url", "url",
@ -2429,6 +2456,16 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.72" version = "2.0.72"
@ -2502,7 +2539,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -2585,7 +2622,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -2713,7 +2750,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]
@ -2819,6 +2856,64 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "utoipa"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23"
dependencies = [
"indexmap",
"serde",
"serde_json",
"utoipa-gen",
]
[[package]]
name = "utoipa-gen"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bf0e16c02bc4bf5322ab65f10ab1149bdbcaa782cba66dc7057370a3f8190be"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"regex",
"syn 2.0.72",
"uuid",
]
[[package]]
name = "utoipauto"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4713aabc5ed18aabcd594345b48983b112c0b5dab3d24754352e7f5cf924da03"
dependencies = [
"utoipauto-macro",
]
[[package]]
name = "utoipauto-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17e82ab96c5a55263b5bed151b8426410d93aa909a453acdbd4b6792b5af7d64"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]]
name = "utoipauto-macro"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8338dc3c9526011ffaa2aa6bd60ddfda9d49d2123108690755c6e34844212"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"utoipauto-core",
]
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.10.0" version = "1.10.0"
@ -2889,7 +2984,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2923,7 +3018,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3177,7 +3272,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.72",
] ]
[[package]] [[package]]

View File

@ -51,4 +51,6 @@ tracing-subscriber = { version = "0.3", features = [
"parking_lot", "parking_lot",
"env-filter", "env-filter",
] } ] }
utoipa = { version = "4", features = ["axum_extras", "uuid", "chrono"] }
utoipauto = "0.1"
uuid = { version = "1", features = ["serde", "v4"] } uuid = { version = "1", features = ["serde", "v4"] }

View File

@ -7,3 +7,5 @@ DROP TABLE folders;
DROP TABLE users; DROP TABLE users;
DROP TYPE permission; DROP TYPE permission;
DROP EXTENSION pg_trgm;

View File

@ -1,3 +1,5 @@
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE TABLE CREATE TABLE
users ( users (
user_id SERIAL PRIMARY KEY, user_id SERIAL PRIMARY KEY,

16
sql/delete_user.sql Normal file
View File

@ -0,0 +1,16 @@
WITH
deleted_files AS (
DELETE FROM files USING folders
WHERE
files.folder_id = folders.folder_id
AND folders.owner_id = $1 RETURNING files.file_id
),
deleted_users AS (
DELETE FROM users
WHERE
user_id = $1
)
SELECT
*
FROM
deleted_files;

9
sql/search_for_user.sql Normal file
View File

@ -0,0 +1,9 @@
SELECT
user_id, username, email
FROM
users
ORDER BY
GREATEST (
similarity (email, $1),
similarity (username, $1)
) DESC

View File

@ -1,6 +1,6 @@
use std::collections::HashSet; use std::collections::HashSet;
use futures::TryStreamExt; use futures::{Stream, TryStreamExt};
use uuid::Uuid; use uuid::Uuid;
use crate::{db::permissions::PermissionRaw, Pool}; use crate::{db::permissions::PermissionRaw, Pool};
@ -101,10 +101,8 @@ pub async fn insert(
.map(|record| record.folder_id) .map(|record| record.folder_id)
} }
pub async fn delete(folder_id: Uuid, pool: &Pool) -> sqlx::Result<Vec<Uuid>> { pub fn delete(folder_id: Uuid, pool: &Pool) -> impl Stream<Item = sqlx::Result<Uuid>> + '_ {
sqlx::query_file!("sql/delete_folder.sql", folder_id) sqlx::query_file!("sql/delete_folder.sql", folder_id)
.fetch(pool) .fetch(pool)
.map_ok(|row| row.file_id) .map_ok(|row| row.file_id)
.try_collect()
.await
} }

View File

@ -1,3 +1,4 @@
pub mod file; pub mod file;
pub mod folder; pub mod folder;
pub mod permissions; pub mod permissions;
pub mod users;

73
src/db/users.rs Normal file
View File

@ -0,0 +1,73 @@
use futures::{stream::BoxStream, Stream, TryStreamExt};
use serde::Serialize;
use uuid::Uuid;
use crate::Pool;
/// Creates user and returns its id
pub async fn create_user(user_name: &str, user_email: &str, pool: &Pool) -> sqlx::Result<i32> {
let id = sqlx::query!(
"INSERT INTO users(username, email) VALUES ($1, $2) RETURNING user_id",
user_name,
user_email
)
.fetch_one(pool)
.await?
.user_id;
sqlx::query!(
"INSERT INTO folders(owner_id, folder_name) VALUES ($1, $2)",
id,
"ROOT"
)
.execute(pool)
.await?;
Ok(id)
}
/// Deletes the user and returns the files that must be deleted
pub fn delete_user(user_id: i32, pool: &Pool) -> impl Stream<Item = sqlx::Result<Uuid>> + '_ {
sqlx::query_file!("sql/delete_user.sql", user_id)
.fetch(pool)
.map_ok(|record| record.file_id)
}
#[derive(Serialize, Debug)]
pub struct UserInfo {
user_id: i32,
username: String,
email: String,
}
pub async fn update(
user_id: i32,
username: &str,
email: &str,
pool: &Pool,
) -> sqlx::Result<UserInfo> {
sqlx::query_as!(
UserInfo,
"UPDATE users SET username = $2, email = $3 WHERE user_id = $1 RETURNING *",
user_id,
username,
email
)
.fetch_one(pool)
.await
}
pub async fn get(user_id: i32, pool: &Pool) -> sqlx::Result<UserInfo> {
sqlx::query_as!(
UserInfo,
"SELECT user_id, username, email FROM users WHERE user_id = $1",
user_id
)
.fetch_one(pool)
.await
}
pub fn search_for_user<'a>(
search_string: &str,
pool: &'a Pool,
) -> BoxStream<'a, sqlx::Result<UserInfo>> {
sqlx::query_file_as!(UserInfo, "sql/search_for_user.sql", search_string).fetch(pool)
}

View File

@ -1,3 +1,5 @@
use futures::TryStreamExt;
use crate::prelude::*; use crate::prelude::*;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -22,14 +24,12 @@ pub async fn delete(
.handle_internal()? .handle_internal()?
.can_write_guard()?; .can_write_guard()?;
let files_to_delete = db::folder::delete(params.folder_id, &state.pool)
.await
.handle_internal()?;
let storage = &state.storage; let storage = &state.storage;
futures::stream::iter(files_to_delete) db::folder::delete(params.folder_id, &state.pool)
.for_each_concurrent(5, |file| async move { .try_for_each_concurrent(5, |file| async move {
let _ = storage.delete(file).await; let _ = storage.delete(file).await;
Ok(())
}) })
.await; .await
Ok(()) .handle_internal()
} }

View File

@ -1,3 +1,4 @@
pub mod file; pub mod file;
pub mod folder; pub mod folder;
pub mod permissions; pub mod permissions;
pub mod users;

View File

@ -1,4 +1,4 @@
pub mod delete; pub mod delete;
pub mod get; pub mod get;
pub mod get_top_level_permitted_folders; pub mod get_top_level;
pub mod set; pub mod set;

View File

@ -0,0 +1,17 @@
use crate::prelude::*;
#[derive(Deserialize, Debug)]
pub struct Params {
username: String,
email: String,
}
pub async fn create(
State(state): State<AppState>,
Json(params): Json<Params>,
) -> Result<Json<i32>, StatusCode> {
let id = db::users::create_user(&params.username, &params.email, &state.pool)
.await
.handle_internal()?;
Ok(Json(id))
}

View File

@ -0,0 +1,14 @@
use futures::TryStreamExt;
use crate::prelude::*;
pub async fn delete(State(state): State<AppState>, claims: Claims) -> Result<(), StatusCode> {
let storage = &state.storage;
db::users::delete_user(claims.user_id, &state.pool)
.try_for_each_concurrent(5, |file_id| async move {
let _ = storage.delete(file_id).await;
Ok(())
})
.await
.handle_internal()
}

View File

@ -0,0 +1,16 @@
use crate::prelude::*;
#[derive(Deserialize, Debug)]
pub struct Params {
user_id: i32,
}
pub async fn get(
State(state): State<AppState>,
Query(params): Query<Params>,
) -> Result<Json<db::users::UserInfo>, StatusCode> {
let info = db::users::get(params.user_id, &state.pool)
.await
.handle_internal()?;
Ok(Json(info))
}

View File

@ -0,0 +1,5 @@
pub mod create;
pub mod delete;
pub mod get;
pub mod put;
pub mod search;

View File

@ -0,0 +1,18 @@
use crate::prelude::*;
#[derive(Deserialize, Debug)]
pub struct Params {
username: String,
email: String,
}
pub async fn put(
State(state): State<AppState>,
claims: Claims,
Json(params): Json<Params>,
) -> Result<Json<db::users::UserInfo>, StatusCode> {
let info = db::users::update(claims.user_id, &params.username, &params.email, &state.pool)
.await
.handle_internal()?;
Ok(Json(info))
}

View File

@ -0,0 +1,20 @@
use futures::TryStreamExt;
use crate::prelude::*;
#[derive(Deserialize, Debug)]
pub struct Params {
search_string: String,
}
pub async fn search(
State(state): State<AppState>,
Query(params): Query<Params>,
) -> sqlx::Result<Json<Vec<db::users::UserInfo>>, StatusCode> {
let users = db::users::search_for_user(&params.search_string, &state.pool)
.take(5)
.try_collect()
.await
.handle_internal()?;
Ok(Json(users))
}

View File

@ -19,25 +19,6 @@ struct AppState {
storage: FileStorage, storage: FileStorage,
} }
async fn create_user(user_name: &str, user_email: &str, pool: &Pool) -> anyhow::Result<i32> {
let id = sqlx::query!(
"INSERT INTO users(username, email) VALUES ($1, $2) RETURNING user_id",
user_name,
user_email
)
.fetch_one(pool)
.await?
.user_id;
sqlx::query!(
"INSERT INTO folders(owner_id, folder_name) VALUES ($1, $2)",
id,
"ROOT"
)
.execute(pool)
.await?;
Ok(id)
}
async fn create_debug_users(pool: &Pool) -> anyhow::Result<()> { async fn create_debug_users(pool: &Pool) -> anyhow::Result<()> {
let count = sqlx::query!("SELECT count(user_id) FROM users") let count = sqlx::query!("SELECT count(user_id) FROM users")
.fetch_one(pool) .fetch_one(pool)
@ -49,8 +30,8 @@ async fn create_debug_users(pool: &Pool) -> anyhow::Result<()> {
} }
tokio::try_join!( tokio::try_join!(
create_user("Test1", "test1@example.com", pool), db::users::create_user("Test1", "test1@example.com", pool),
create_user("Test2", "test2@example.com", pool) db::users::create_user("Test2", "test2@example.com", pool)
)?; )?;
Ok(()) Ok(())
@ -87,7 +68,8 @@ fn app(state: AppState) -> Router {
use axum::{http::header, routing::get}; use axum::{http::header, routing::get};
use endpoints::{ use endpoints::{
file, folder, file, folder,
permissions::{self, get_top_level_permitted_folders::get_top_level}, permissions::{self, get_top_level::get_top_level},
users,
}; };
use tower_http::ServiceBuilderExt as _; use tower_http::ServiceBuilderExt as _;
@ -123,6 +105,14 @@ fn app(state: AppState) -> Router {
"/permissions/get_top_level_permitted_folders", "/permissions/get_top_level_permitted_folders",
get(get_top_level), get(get_top_level),
) )
.route(
"/users",
get(users::get::get)
.post(users::create::create)
.delete(users::delete::delete)
.put(users::put::put),
)
.route("/users/search", get(users::search::search))
.layer(middleware) .layer(middleware)
.with_state(state) .with_state(state)
} }