Compare commits

..

No commits in common. "32d207a991c77c516ef3b535c527712ad6bdb7bb" and "33356f34e8ef81b073c7a15a63c9c65c05e4eac0" have entirely different histories.

46 changed files with 328 additions and 759 deletions

View File

@ -1,36 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE users SET username = $2, email = $3 WHERE user_id = $1 RETURNING *",
"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": [
"Int4",
"Varchar",
"Varchar"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "347a486f9ea5183b1c4c16234a1833ea61970ea7f901dd57c0715ae3dbddd164"
}

View File

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

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

View File

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

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

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

View File

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

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

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

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

View File

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

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

View File

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

View File

@ -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"
}

View File

@ -1,34 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT user_id, username, email FROM users WHERE user_id = $1",
"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": [
"Int4"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "fd53e5f2c7e8aa87f3cf4e430a6ec3a632ce125fdb092dbd17630e952d4e0d9e"
}

297
Cargo.lock generated
View File

@ -26,7 +26,7 @@ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy 0.7.35", "zerocopy",
] ]
[[package]] [[package]]
@ -107,7 +107,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -220,7 +220,7 @@ dependencies = [
"heck 0.4.1", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -321,9 +321,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.7.0" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
[[package]] [[package]]
name = "cc" name = "cc"
@ -572,6 +572,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.1"
@ -648,7 +663,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -987,6 +1002,22 @@ dependencies = [
"webpki-roots 0.26.3", "webpki-roots 0.26.3",
] ]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper 1.4.1",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.6" version = "0.1.6"
@ -1048,7 +1079,6 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@ -1231,6 +1261,23 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "native-tls"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -1349,6 +1396,50 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -1432,7 +1523,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -1482,35 +1573,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.18" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b"
dependencies = [ dependencies = [
"zerocopy 0.6.6", "zerocopy",
] "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]]
@ -1544,8 +1612,6 @@ dependencies = [
"tower-http", "tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"utoipa",
"utoipauto",
"uuid", "uuid",
] ]
@ -1745,6 +1811,7 @@ checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2 0.4.5", "h2 0.4.5",
@ -1753,11 +1820,13 @@ dependencies = [
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.4.1",
"hyper-rustls 0.27.2", "hyper-rustls 0.27.2",
"hyper-tls",
"hyper-util", "hyper-util",
"ipnet", "ipnet",
"js-sys", "js-sys",
"log", "log",
"mime", "mime",
"native-tls",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
@ -1769,7 +1838,9 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper 1.0.1", "sync_wrapper 1.0.1",
"system-configuration",
"tokio", "tokio",
"tokio-native-tls",
"tokio-rustls 0.26.0", "tokio-rustls 0.26.0",
"tower-service", "tower-service",
"url", "url",
@ -1924,6 +1995,15 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "schannel"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@ -1940,6 +2020,29 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.204" version = "1.0.204"
@ -1957,7 +2060,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -2171,7 +2274,7 @@ dependencies = [
"quote", "quote",
"sqlx-core", "sqlx-core",
"sqlx-macros-core", "sqlx-macros-core",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -2194,7 +2297,7 @@ dependencies = [
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres", "sqlx-postgres",
"sqlx-sqlite", "sqlx-sqlite",
"syn 2.0.72", "syn",
"tempfile", "tempfile",
"tokio", "tokio",
"url", "url",
@ -2326,16 +2429,6 @@ 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"
@ -2409,7 +2502,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -2492,7 +2585,17 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
] ]
[[package]] [[package]]
@ -2610,7 +2713,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]
@ -2716,64 +2819,6 @@ 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"
@ -2844,7 +2889,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2878,7 +2923,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3114,34 +3159,14 @@ dependencies = [
"windows-sys 0.48.0", "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]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.7.35" version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [ dependencies = [
"zerocopy-derive 0.7.35", "byteorder",
] "zerocopy-derive",
[[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]] [[package]]
@ -3152,7 +3177,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.72", "syn",
] ]
[[package]] [[package]]

View File

@ -26,11 +26,7 @@ dotenvy = "0.15"
futures = "0.3" futures = "0.3"
jsonwebtoken = "9" jsonwebtoken = "9"
oauth2 = "4" oauth2 = "4"
reqwest = { version = "0.12", features = [ reqwest = { version = "0.12", features = ["http2", "rustls-tls", "json"] }
"http2",
"rustls-tls",
"json",
], default-features = false }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
sha2 = "0.10" sha2 = "0.10"
sqlx = { version = "0.8", features = [ sqlx = { version = "0.8", features = [
@ -55,6 +51,4 @@ tracing-subscriber = { version = "0.3", features = [
"parking_lot", "parking_lot",
"env-filter", "env-filter",
] } ] }
utoipa = { version = "4", features = ["axum_extras", "uuid", "chrono"] } uuid = { version = "1", features = ["serde", "v4"] }
utoipauto = "0.1"
uuid = { version = "1", features = ["serde", "v7"] }

View File

@ -1,6 +1,6 @@
services: services:
db: db:
image: ghcr.io/fboulnois/pg_uuidv7:1.5.0 image: postgres:alpine
environment: environment:
- POSTGRES_USER=tester - POSTGRES_USER=tester
- POSTGRES_PASSWORD=testing123! - POSTGRES_PASSWORD=testing123!

View File

@ -1,7 +1,3 @@
DROP EXTENSION IF EXISTS pg_trgm;
DROP EXTENSION IF EXISTS pg_uuidv7;
DROP TABLE permissions; DROP TABLE permissions;
DROP TABLE files; DROP TABLE files;

View File

@ -1,7 +1,3 @@
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS pg_uuidv7;
CREATE TABLE CREATE TABLE
users ( users (
user_id SERIAL PRIMARY KEY, user_id SERIAL PRIMARY KEY,
@ -11,7 +7,7 @@ CREATE TABLE
CREATE TABLE CREATE TABLE
folders ( folders (
folder_id UUID PRIMARY KEY DEFAULT uuid_generate_v7 (), folder_id UUID PRIMARY KEY DEFAULT gen_random_uuid (),
parent_folder_id UUID REFERENCES folders (folder_id) ON DELETE CASCADE DEFAULT null, 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, owner_id INT REFERENCES users (user_id) ON DELETE CASCADE NOT NULL,
folder_name VARCHAR(255) NOT NULL, folder_name VARCHAR(255) NOT NULL,
@ -20,7 +16,7 @@ CREATE TABLE
CREATE TABLE CREATE TABLE
files ( files (
file_id UUID PRIMARY KEY DEFAULT uuid_generate_v7 (), file_id UUID PRIMARY KEY DEFAULT gen_random_uuid (),
folder_id UUID REFERENCES folders (folder_id) ON DELETE CASCADE NOT NULL, folder_id UUID REFERENCES folders (folder_id) ON DELETE CASCADE NOT NULL,
file_name VARCHAR(255) NOT NULL, file_name VARCHAR(255) NOT NULL,
file_size BIGINT NOT NULL, file_size BIGINT NOT NULL,

View File

@ -1,16 +0,0 @@
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;

View File

@ -1,18 +0,0 @@
SELECT
EXISTS (
SELECT
file_name as name
FROM
files
WHERE
folder_id = $1
AND file_name = $2
UNION
SELECT
folder_name as name
FROM
folders
WHERE
parent_folder_id = $1
AND folder_name = $2
)

View File

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

View File

@ -1,4 +1,6 @@
use futures::{Stream, TryStreamExt}; use std::collections::HashSet;
use futures::TryStreamExt;
use uuid::Uuid; use uuid::Uuid;
use crate::{db::permissions::PermissionRaw, Pool}; use crate::{db::permissions::PermissionRaw, Pool};
@ -17,10 +19,12 @@ pub async fn get_permissions(
Ok(permission.into()) Ok(permission.into())
} }
pub fn get_names(folder_id: Uuid, pool: &Pool) -> impl Stream<Item = sqlx::Result<String>> + '_ { pub async fn get_names(folder_id: Uuid, pool: &Pool) -> sqlx::Result<HashSet<String>> {
sqlx::query!("SELECT folder_name as name FROM folders WHERE parent_folder_id = $1 UNION SELECT file_name as name FROM files WHERE folder_id = $1", folder_id) sqlx::query!("SELECT folder_name as name FROM folders WHERE parent_folder_id = $1 UNION SELECT file_name as name FROM files WHERE folder_id = $1", folder_id)
.fetch(pool) .fetch(pool)
.map_ok(|record| record.name.unwrap()) .map_ok(|record| record.name.unwrap())
.try_collect::<HashSet<String>>()
.await
} }
pub async fn get_root(user_id: i32, pool: &Pool) -> sqlx::Result<Uuid> { pub async fn get_root(user_id: i32, pool: &Pool) -> sqlx::Result<Uuid> {
@ -64,11 +68,21 @@ pub async fn get_folders(
.await .await
} }
pub async fn name_exists(parent_folder_id: Uuid, name: &str, pool: &Pool) -> sqlx::Result<bool> { pub async fn exists_by_name(
sqlx::query_file!("sql/name_exists.sql", parent_folder_id, name) parent_folder_id: Uuid,
folder_name: &str,
pool: &Pool,
) -> sqlx::Result<bool> {
sqlx::query!(
"SELECT EXISTS(SELECT folder_id FROM folders WHERE parent_folder_id = $1 AND folder_name = $2)",
parent_folder_id,
folder_name
)
.fetch_one(pool) .fetch_one(pool)
.await .await
.map(|row| row.exists.unwrap_or(false)) .and_then(|row| {
row.exists.ok_or(sqlx::Error::RowNotFound)
})
} }
pub async fn insert( pub async fn insert(
@ -87,8 +101,10 @@ pub async fn insert(
.map(|record| record.folder_id) .map(|record| record.folder_id)
} }
pub fn delete(folder_id: Uuid, pool: &Pool) -> impl Stream<Item = sqlx::Result<Uuid>> + '_ { pub async fn delete(folder_id: Uuid, pool: &Pool) -> sqlx::Result<Vec<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,4 +1,3 @@
pub mod file; pub mod file;
pub mod folder; pub mod folder;
pub mod permissions; pub mod permissions;
pub mod users;

View File

@ -1,73 +0,0 @@
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

@ -18,7 +18,7 @@ pub async fn modify(
.handle_internal()? .handle_internal()?
.can_write_guard()?; .can_write_guard()?;
// Very weird work around to get the first file in multipart // Very weird work around
let mut field = loop { let mut field = loop {
match multipart.next_field().await { match multipart.next_field().await {
Ok(Some(field)) if field.file_name().is_some() => break field, Ok(Some(field)) if field.file_name().is_some() => break field,
@ -34,13 +34,13 @@ pub async fn modify(
.handle_internal()? .handle_internal()?
.ok_or(StatusCode::NOT_FOUND)?; .ok_or(StatusCode::NOT_FOUND)?;
let (hash, size) = crate::FileStorage::write_to_file(&mut file, &mut field) let (hash, size) = match crate::FileStorage::write_to_file(&mut file, &mut field).await {
.await Ok(values) => values,
.map_err(|err| { Err(err) => {
tracing::warn!(%err); tracing::warn!(%err);
StatusCode::INTERNAL_SERVER_ERROR return Err(StatusCode::INTERNAL_SERVER_ERROR);
})?; }
};
db::file::update(params.file_id, size, hash, &state.pool) db::file::update(params.file_id, size, hash, &state.pool)
.await .await
.handle_internal()?; .handle_internal()?;

View File

@ -1,7 +1,6 @@
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use axum::extract::multipart::{self, Multipart}; use axum::extract::multipart::{self, Multipart};
use futures::TryStreamExt;
use tokio::io::AsyncWrite; use tokio::io::AsyncWrite;
use crate::prelude::*; use crate::prelude::*;
@ -43,8 +42,7 @@ pub async fn upload(
.handle_internal()? .handle_internal()?
.can_write_guard()?; .can_write_guard()?;
let existing_names: HashSet<String> = db::folder::get_names(params.parent_folder, &state.pool) let existing_names = db::folder::get_names(params.parent_folder, &state.pool)
.try_collect()
.await .await
.handle_internal()?; .handle_internal()?;
let mut result = HashMap::new(); let mut result = HashMap::new();

View File

@ -7,16 +7,17 @@ pub struct Params {
} }
pub async fn create( pub async fn create(
State(pool): State<Pool>, State(state): State<AppState>,
claims: Claims, claims: Claims,
Json(params): Json<Params>, Json(params): Json<Params>,
) -> Result<Json<Uuid>, StatusCode> { ) -> Result<Json<Uuid>, StatusCode> {
db::folder::get_permissions(params.parent_folder_id, claims.user_id, &pool) db::folder::get_permissions(params.parent_folder_id, claims.user_id, &state.pool)
.await .await
.handle_internal()? .handle_internal()?
.can_write_guard()?; .can_write_guard()?;
let exists = db::folder::name_exists(params.parent_folder_id, &params.folder_name, &pool) let exists =
db::folder::exists_by_name(params.parent_folder_id, &params.folder_name, &state.pool)
.await .await
.handle_internal()?; .handle_internal()?;
if exists { if exists {
@ -27,7 +28,7 @@ pub async fn create(
params.parent_folder_id, params.parent_folder_id,
claims.user_id, claims.user_id,
&params.folder_name, &params.folder_name,
&pool, &state.pool,
) )
.await .await
.handle_internal()?; .handle_internal()?;

View File

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

View File

@ -16,17 +16,17 @@ pub struct Response {
pub async fn list( pub async fn list(
Query(params): Query<Params>, Query(params): Query<Params>,
State(pool): State<Pool>, State(state): State<AppState>,
claims: Claims, claims: Claims,
) -> Result<Json<Response>, StatusCode> { ) -> Result<Json<Response>, StatusCode> {
let folder_id = db::folder::get_by_id(params.folder_id, claims.user_id, &pool) let folder_id = db::folder::get_by_id(params.folder_id, claims.user_id, &state.pool)
.await .await
.handle_internal()? .handle_internal()?
.ok_or(StatusCode::NOT_FOUND)?; .ok_or(StatusCode::NOT_FOUND)?;
let (files, folders) = try_join!( let (files, folders) = try_join!(
db::file::get_files(folder_id, &pool), db::file::get_files(folder_id, &state.pool),
db::folder::get_folders(folder_id, &pool) db::folder::get_folders(folder_id, &state.pool)
) )
.handle_internal()?; .handle_internal()?;

View File

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

View File

@ -7,18 +7,18 @@ pub struct Params {
} }
pub async fn delete( pub async fn delete(
State(pool): State<Pool>,
claims: Claims, claims: Claims,
State(state): State<AppState>,
Json(params): Json<Params>, Json(params): Json<Params>,
) -> Result<StatusCode, StatusCode> { ) -> Result<StatusCode, 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, &state.pool)
.await .await
.handle_internal()? .handle_internal()?
.can_manage_guard()?; .can_manage_guard()?;
} }
db::permissions::delete_for_folder(params.folder_id, params.user_id, &pool) db::permissions::delete_for_folder(params.folder_id, params.user_id, &state.pool)
.await .await
.handle_internal()?; .handle_internal()?;

View File

@ -10,16 +10,16 @@ pub struct Params {
} }
pub async fn get( pub async fn get(
State(pool): State<Pool>,
Query(params): Query<Params>, Query(params): Query<Params>,
claims: Claims, claims: Claims,
State(state): State<AppState>,
) -> Result<Json<HashMap<String, PermissionRaw>>, StatusCode> { ) -> Result<Json<HashMap<String, PermissionRaw>>, StatusCode> {
db::folder::get_permissions(params.folder_id, claims.user_id, &pool) db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool)
.await .await
.handle_internal()? .handle_internal()?
.can_manage_guard()?; .can_manage_guard()?;
let permissions = db::permissions::get_all_for_folder(params.folder_id, &pool) let permissions = db::permissions::get_all_for_folder(params.folder_id, &state.pool)
.await .await
.handle_internal()?; .handle_internal()?;
Ok(Json(permissions)) Ok(Json(permissions))

View File

@ -1,10 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
pub async fn get_top_level( pub async fn get_top_level(
State(pool): State<Pool>, State(state): State<AppState>,
claims: Claims, claims: Claims,
) -> Result<Json<Vec<Uuid>>, StatusCode> { ) -> Result<Json<Vec<Uuid>>, StatusCode> {
let folders = db::permissions::get_top_level_permitted_folders(claims.user_id, &pool) let folders = db::permissions::get_top_level_permitted_folders(claims.user_id, &state.pool)
.await .await
.handle_internal()?; .handle_internal()?;
Ok(Json(folders)) Ok(Json(folders))

View File

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

View File

@ -11,17 +11,17 @@ pub struct Params {
pub async fn set( pub async fn set(
claims: Claims, claims: Claims,
State(pool): State<Pool>, State(state): State<AppState>,
Json(params): Json<Params>, Json(params): Json<Params>,
) -> Result<StatusCode, StatusCode> { ) -> Result<StatusCode, StatusCode> {
let root = db::folder::get_root(claims.user_id, &pool) let root = db::folder::get_root(claims.user_id, &state.pool)
.await .await
.handle_internal()?; .handle_internal()?;
if params.folder_id == root { if params.folder_id == root {
return Err(StatusCode::BAD_REQUEST); return Err(StatusCode::BAD_REQUEST);
} }
db::folder::get_permissions(params.folder_id, claims.user_id, &pool) db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool)
.await .await
.handle_internal()? .handle_internal()?
.can_manage_guard()?; .can_manage_guard()?;
@ -30,7 +30,7 @@ pub async fn set(
params.user_id, params.user_id,
params.folder_id, params.folder_id,
params.permission_type, params.permission_type,
&pool, &state.pool,
) )
.await .await
.handle_internal()?; .handle_internal()?;

View File

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

View File

@ -1,16 +0,0 @@
use futures::TryStreamExt;
use crate::prelude::*;
pub async fn delete(
State(AppState { pool, ref storage }): State<AppState>,
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(())
})
.await
.handle_internal()
}

View File

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

View File

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

View File

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

View File

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

View File

@ -21,7 +21,7 @@ impl FileStorage {
pub fn new() -> anyhow::Result<Self> { pub fn new() -> anyhow::Result<Self> {
let var = env::var("DRIVE_STORAGE_PATH"); let var = env::var("DRIVE_STORAGE_PATH");
let path_str = match var { let path_str = match var {
Ok(ref string) => string.trim(), Ok(ref string) => string,
Err(err) => { Err(err) => {
tracing::info!( tracing::info!(
%err, %err,
@ -55,7 +55,7 @@ impl FileStorage {
pub async fn create(&self) -> anyhow::Result<(Uuid, impl tokio::io::AsyncWrite)> { pub async fn create(&self) -> anyhow::Result<(Uuid, impl tokio::io::AsyncWrite)> {
let mut error = anyhow::anyhow!("Error creating a file"); let mut error = anyhow::anyhow!("Error creating a file");
for _ in 0..3 { for _ in 0..3 {
let file_id = Uuid::now_v7(); let file_id = Uuid::new_v4();
match self.create_inner(file_id).await { match self.create_inner(file_id).await {
Ok(file) => return Ok((file_id, file)), Ok(file) => return Ok((file_id, file)),
Err(err) => error = error.context(err), Err(err) => error = error.context(err),

View File

@ -7,7 +7,7 @@ mod prelude;
use std::{env, net::Ipv4Addr}; use std::{env, net::Ipv4Addr};
use axum::{extract::FromRef, Router}; use axum::Router;
use file_storage::FileStorage; use file_storage::FileStorage;
use tokio::net::TcpListener; use tokio::net::TcpListener;
@ -19,13 +19,26 @@ struct AppState {
storage: FileStorage, storage: FileStorage,
} }
impl FromRef<AppState> for Pool { async fn create_user(user_name: &str, user_email: &str, pool: &Pool) -> anyhow::Result<i32> {
fn from_ref(input: &AppState) -> Self { let id = sqlx::query!(
input.pool.clone() "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_test_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)
.await? .await?
@ -36,8 +49,8 @@ async fn create_test_users(pool: &Pool) -> anyhow::Result<()> {
} }
tokio::try_join!( tokio::try_join!(
db::users::create_user("Test1", "test1@example.com", pool), create_user("Test1", "test1@example.com", pool),
db::users::create_user("Test2", "test2@example.com", pool) create_user("Test2", "test2@example.com", pool)
)?; )?;
Ok(()) Ok(())
@ -55,14 +68,11 @@ async fn main() -> anyhow::Result<()> {
Err(err) => anyhow::bail!("Error getting database url: {err}"), Err(err) => anyhow::bail!("Error getting database url: {err}"),
}; };
sqlx::migrate!().run(&pool).await?; sqlx::migrate!().run(&pool).await?;
if let Ok("1") = env::var("DEVELOPMENT").as_deref().map(str::trim_ascii) { create_debug_users(&pool).await?;
create_test_users(&pool).await?;
}
let state = AppState { let storage = file_storage::FileStorage::new()?;
pool,
storage: FileStorage::new()?, let state = AppState { pool, storage };
};
let router = app(state); let router = app(state);
let addr = (Ipv4Addr::UNSPECIFIED, 3000); let addr = (Ipv4Addr::UNSPECIFIED, 3000);
@ -77,8 +87,7 @@ 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::get_top_level}, permissions::{self, get_top_level_permitted_folders::get_top_level},
users,
}; };
use tower_http::ServiceBuilderExt as _; use tower_http::ServiceBuilderExt as _;
@ -114,14 +123,6 @@ 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)
} }