Compare commits
6 Commits
1e2df49ced
...
2eee67bdd1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eee67bdd1 | ||
|
|
5023028c50 | ||
|
|
6c850263ad | ||
|
|
dfbc5ad6b0 | ||
|
|
897bbeb198 | ||
|
|
d86ac5822c |
307
Cargo.lock
generated
307
Cargo.lock
generated
@ -39,6 +39,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
|
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
"axum-macros",
|
||||||
"bytes",
|
"bytes",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -51,6 +52,7 @@ dependencies = [
|
|||||||
"matchit",
|
"matchit",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mime",
|
"mime",
|
||||||
|
"multer",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
@ -86,6 +88,17 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-macros"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.74"
|
version = "0.3.74"
|
||||||
@ -125,8 +138,15 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"figment",
|
"figment",
|
||||||
|
"futures",
|
||||||
|
"futures-util",
|
||||||
|
"problem_details",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tracing",
|
||||||
|
"tracing-log",
|
||||||
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -135,6 +155,15 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding_rs"
|
||||||
|
version = "0.8.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -148,6 +177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
|
checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic",
|
"atomic",
|
||||||
|
"pear",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
"uncased",
|
"uncased",
|
||||||
@ -169,6 +199,21 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -176,6 +221,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -184,6 +230,40 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -196,10 +276,16 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -248,6 +334,16 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-serde"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.9.5"
|
version = "1.9.5"
|
||||||
@ -305,12 +401,24 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inlinable_string"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.169"
|
version = "0.2.169"
|
||||||
@ -371,6 +479,33 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "multer"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"encoding_rs",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"httparse",
|
||||||
|
"memchr",
|
||||||
|
"mime",
|
||||||
|
"spin",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@ -386,6 +521,12 @@ version = "1.20.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@ -409,6 +550,29 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pear"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c"
|
||||||
|
dependencies = [
|
||||||
|
"inlinable_string",
|
||||||
|
"pear_codegen",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pear_codegen"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"proc-macro2-diagnostics",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -427,6 +591,19 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "problem_details"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ee2055bf7d8f34bd11bf85388b878bf244256015f1ed290b6b717c0e97bb478"
|
||||||
|
dependencies = [
|
||||||
|
"axum",
|
||||||
|
"http",
|
||||||
|
"http-serde",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.92"
|
version = "1.0.92"
|
||||||
@ -436,6 +613,19 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2-diagnostics"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.38"
|
version = "1.0.38"
|
||||||
@ -541,6 +731,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -550,6 +749,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
@ -566,6 +774,12 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.95"
|
version = "2.0.95"
|
||||||
@ -583,6 +797,16 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.42.0"
|
version = "1.42.0"
|
||||||
@ -612,6 +836,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-stream"
|
||||||
|
version = "0.1.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.19"
|
version = "0.8.19"
|
||||||
@ -682,9 +917,21 @@ checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.33"
|
version = "0.1.33"
|
||||||
@ -692,6 +939,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||||
|
dependencies = [
|
||||||
|
"nu-ansi-term",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -709,6 +982,12 @@ version = "1.0.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@ -721,6 +1000,28 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@ -802,3 +1103,9 @@ checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "1.0.0-rc"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ee746ad3851dd3bc40e4a028ab3b00b99278d929e48957bcb2d111874a7e43e"
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
port = 3000
|
|
||||||
listen = "0.0.0.0"
|
|
||||||
@ -2,9 +2,18 @@
|
|||||||
name = "casket-backend"
|
name = "casket-backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
description = "Backend for the casket file cloud."
|
||||||
|
repository = "https://git.nifni.eu/nif/casket"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.8.1" }
|
axum = { version = "0.8.1", features = ["multipart", "macros"] }
|
||||||
tokio = { version = "1.42.0", features = ["full"] }
|
tokio = { version = "1.42.0", features = ["full"] }
|
||||||
figment = { version = "0.10.19", features = ["toml"] }
|
tokio-stream = "0.1.17"
|
||||||
serde = {version = "1.0.217", features = ["derive"]}
|
futures = "0.3"
|
||||||
|
futures-util = "0.3"
|
||||||
|
figment = { version = "0.10.19", features = ["toml", "env"] }
|
||||||
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
|
tracing = "0.1.41"
|
||||||
|
tracing-log = "0.2.0"
|
||||||
|
tracing-subscriber = "0.3.19"
|
||||||
|
problem_details = { version = "0.7.0", features = ["axum"] }
|
||||||
25
casket-backend/README.md
Normal file
25
casket-backend/README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Casket backend
|
||||||
|
|
||||||
|
Backend server for casket.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd casket-backend
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
See [casket.toml](./casket.toml) for an example config file. By default, the following files are checked
|
||||||
|
to exist:
|
||||||
|
|
||||||
|
- `./casket-backend/casket.toml`
|
||||||
|
- `/config/casket.toml`
|
||||||
|
|
||||||
|
Additionally, it is possible to provide configs via environment variables. Use the prefix `CASKET_`.
|
||||||
|
For nested configs use two underscores to mark the place to split:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CASKET_SERVER__port=1234
|
||||||
|
```
|
||||||
6
casket-backend/casket.toml
Normal file
6
casket-backend/casket.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[server]
|
||||||
|
port = 3000
|
||||||
|
bind_address = "127.0.0.1"
|
||||||
|
|
||||||
|
[files]
|
||||||
|
directory = "/tmp/casket"
|
||||||
@ -1,15 +1,32 @@
|
|||||||
|
use figment::providers::{Env, Format};
|
||||||
use figment::{providers::Toml, Figment};
|
use figment::{providers::Toml, Figment};
|
||||||
use figment::providers::Format;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub const CONFIG_LOCATIONS: [&str; 2] = ["casket-backend/casket.toml", "/config/casket.toml"];
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
port: u16,
|
pub server: Server,
|
||||||
listen: String,
|
pub files: Files,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
pub struct Server {
|
||||||
|
pub port: u16,
|
||||||
|
pub bind_address: String,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
pub struct Files {
|
||||||
|
pub directory: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config() -> figment::Result<Config> {
|
pub fn get_config() -> figment::Result<Config> {
|
||||||
Figment::new()
|
CONFIG_LOCATIONS
|
||||||
.merge(Toml::file("application.toml"))
|
.iter()
|
||||||
|
.fold(Figment::new(), |figment, location| {
|
||||||
|
figment.merge(Toml::file(location))
|
||||||
|
})
|
||||||
|
.join(Env::prefixed("CASKET_").split("__"))
|
||||||
.extract()
|
.extract()
|
||||||
}
|
}
|
||||||
|
|||||||
17
casket-backend/src/errors.rs
Normal file
17
casket-backend/src/errors.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use axum::http::StatusCode;
|
||||||
|
use problem_details::ProblemDetails;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
pub const ERROR_DETAILS_PATH_TRAVERSAL: &str = "You must not traverse!";
|
||||||
|
pub const ERROR_DETAILS_INTERNAL_ERROR: &str =
|
||||||
|
"Internal Error! Check the logs of the backend service for details.";
|
||||||
|
pub const ERROR_DETAILS_ABSOLUTE_PATH_NOT_ALLOWED: &str = "Absolute paths are not allowed.";
|
||||||
|
pub const ERROR_DETAILS_NO_NAME_PROVIDED_MULTIPART_FIELD: &str =
|
||||||
|
"No name provided for multipart form field";
|
||||||
|
|
||||||
|
pub fn to_internal_error(error: impl Debug) -> ProblemDetails {
|
||||||
|
error!("{:?}", error);
|
||||||
|
ProblemDetails::from_status_code(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
.with_detail(ERROR_DETAILS_INTERNAL_ERROR)
|
||||||
|
}
|
||||||
24
casket-backend/src/extractor_helper.rs
Normal file
24
casket-backend/src/extractor_helper.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use axum::extract::FromRequestParts;
|
||||||
|
use axum::http::request::Parts;
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use problem_details::ProblemDetails;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
pub struct WithProblemDetails<T>(pub T);
|
||||||
|
|
||||||
|
impl<T, S> FromRequestParts<S> for WithProblemDetails<T>
|
||||||
|
where
|
||||||
|
T: FromRequestParts<S>,
|
||||||
|
T::Rejection: Debug,
|
||||||
|
S: Sync,
|
||||||
|
{
|
||||||
|
type Rejection = ProblemDetails;
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
match T::from_request_parts(parts, state).await {
|
||||||
|
Ok(inner) => Ok(Self(inner)),
|
||||||
|
Err(rejection) => Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||||
|
.with_detail(format!("{rejection:?}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
107
casket-backend/src/files/list.rs
Normal file
107
casket-backend/src/files/list.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use crate::errors::to_internal_error;
|
||||||
|
use crate::extractor_helper::WithProblemDetails;
|
||||||
|
use crate::files::build_system_path;
|
||||||
|
use crate::{errors, AppState};
|
||||||
|
use axum::extract::{Query, State};
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use axum::{debug_handler, Json};
|
||||||
|
use problem_details::ProblemDetails;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::io::Error;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tokio::fs::{read_dir, DirEntry};
|
||||||
|
use tracing::{debug, error};
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn query_file_tree(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
axum::extract::Path(user_id): axum::extract::Path<String>,
|
||||||
|
WithProblemDetails(Query(query)): WithProblemDetails<Query<FileQuery>>,
|
||||||
|
) -> Result<Json<PathElements>, ProblemDetails> {
|
||||||
|
let path = build_system_path(&state.config.files.directory, &user_id, &query.path)?;
|
||||||
|
debug!("Loading path: {:?}", path);
|
||||||
|
Ok(PathElements::new(load_directory(path, query.nesting).await?).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load_directory(path: PathBuf, nesting: u32) -> Result<Vec<PathElement>, ProblemDetails> {
|
||||||
|
match read_dir(path).await {
|
||||||
|
Err(error) => Err(map_io_error(error)),
|
||||||
|
Ok(mut value) => {
|
||||||
|
let mut result = vec![];
|
||||||
|
while let Some(next) = value.next_entry().await.map_err(map_io_error)? {
|
||||||
|
result.push(PathElement::from(next, nesting).await?);
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_io_error(error: Error) -> ProblemDetails {
|
||||||
|
match error.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => ProblemDetails::from_status_code(StatusCode::NOT_FOUND),
|
||||||
|
_ => to_internal_error(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct FileQuery {
|
||||||
|
path: PathBuf,
|
||||||
|
#[serde(default = "u32::min_value")]
|
||||||
|
nesting: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PathElements {
|
||||||
|
files: Vec<PathElement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathElements {
|
||||||
|
pub fn new(files: Vec<PathElement>) -> Self {
|
||||||
|
Self { files }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PathElement {
|
||||||
|
name: String,
|
||||||
|
is_dir: bool,
|
||||||
|
children: Option<Vec<PathElement>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathElement {
|
||||||
|
async fn from(value: DirEntry, nesting: u32) -> Result<Self, ProblemDetails> {
|
||||||
|
let metadata = value.metadata().await.unwrap();
|
||||||
|
debug!("File type: {:?}", value.file_type().await.unwrap());
|
||||||
|
let is_dir = metadata.is_dir();
|
||||||
|
if is_dir || metadata.is_file() {
|
||||||
|
Ok(PathElement {
|
||||||
|
name: value.file_name().into_string().unwrap(),
|
||||||
|
is_dir,
|
||||||
|
children: load_children(value.path(), nesting, is_dir).await?,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"File <{:?}> is neither a directory nor a file. ",
|
||||||
|
value.file_name()
|
||||||
|
);
|
||||||
|
Err(
|
||||||
|
ProblemDetails::from_status_code(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
.with_detail(errors::ERROR_DETAILS_INTERNAL_ERROR),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn load_children(
|
||||||
|
path: PathBuf,
|
||||||
|
nesting: u32,
|
||||||
|
is_dir: bool,
|
||||||
|
) -> Result<Option<Vec<PathElement>>, ProblemDetails> {
|
||||||
|
if nesting == 0 {
|
||||||
|
Ok(None)
|
||||||
|
} else if !is_dir {
|
||||||
|
Ok(Some(vec![]))
|
||||||
|
} else {
|
||||||
|
Ok(Some(Box::pin(load_directory(path, nesting - 1)).await?))
|
||||||
|
}
|
||||||
|
}
|
||||||
121
casket-backend/src/files/mod.rs
Normal file
121
casket-backend/src/files/mod.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
use crate::errors;
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use problem_details::ProblemDetails;
|
||||||
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
|
pub mod list;
|
||||||
|
pub mod upload;
|
||||||
|
|
||||||
|
fn build_system_path(
|
||||||
|
base_directory: &Path,
|
||||||
|
user_id: &str,
|
||||||
|
user_path: &PathBuf,
|
||||||
|
) -> Result<PathBuf, ProblemDetails> {
|
||||||
|
if user_path.is_absolute() {
|
||||||
|
Err(
|
||||||
|
ProblemDetails::from_status_code(StatusCode::BAD_REQUEST).with_detail(
|
||||||
|
errors::ERROR_DETAILS_ABSOLUTE_PATH_NOT_ALLOWED.to_owned()
|
||||||
|
+ format!(" Provided Path: {user_path:?}",).as_str(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
sanitize_path(&base_directory.join(user_id).join(user_path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sanitize_path(path: &Path) -> Result<PathBuf, ProblemDetails> {
|
||||||
|
let mut ret = PathBuf::new();
|
||||||
|
for component in path.components().peekable() {
|
||||||
|
match component {
|
||||||
|
Component::Prefix(..) => unreachable!(),
|
||||||
|
Component::RootDir => {
|
||||||
|
ret.push(Component::RootDir);
|
||||||
|
}
|
||||||
|
Component::CurDir => {}
|
||||||
|
Component::ParentDir => {
|
||||||
|
return Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||||
|
.with_detail(errors::ERROR_DETAILS_PATH_TRAVERSAL));
|
||||||
|
}
|
||||||
|
Component::Normal(c) => {
|
||||||
|
ret.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sanitize_path_success() {
|
||||||
|
let cases = &[
|
||||||
|
("", ""),
|
||||||
|
(".", ""),
|
||||||
|
(".////./.", ""),
|
||||||
|
("/", "/"),
|
||||||
|
("/foo/bar", "/foo/bar"),
|
||||||
|
("/foo/bar/", "/foo/bar"),
|
||||||
|
("/foo/bar/./././///", "/foo/bar"),
|
||||||
|
("foo/bar", "foo/bar"),
|
||||||
|
("foo/bar/", "foo/bar"),
|
||||||
|
("foo/bar/./././///", "foo/bar"),
|
||||||
|
];
|
||||||
|
for (input, expected) in cases {
|
||||||
|
let actual = sanitize_path(&PathBuf::from(input));
|
||||||
|
assert_eq!(actual, Ok(PathBuf::from(expected)), "input: {input}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sanitize_path_error() {
|
||||||
|
let cases = &[
|
||||||
|
"..",
|
||||||
|
"/../",
|
||||||
|
"../../foo/bar",
|
||||||
|
"../../foo/bar/",
|
||||||
|
"../../foo/bar/./././///",
|
||||||
|
"../../foo/bar/..",
|
||||||
|
"../../foo/bar/../..",
|
||||||
|
"../../foo/bar/../../..",
|
||||||
|
"/foo/bar/../../..",
|
||||||
|
"/foo/bar/../..",
|
||||||
|
"/foo/bar/..",
|
||||||
|
];
|
||||||
|
for input in cases {
|
||||||
|
let actual = sanitize_path(&PathBuf::from(input));
|
||||||
|
assert_eq!(
|
||||||
|
actual,
|
||||||
|
Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||||
|
.with_detail(errors::ERROR_DETAILS_PATH_TRAVERSAL)),
|
||||||
|
"input: {input}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_build_system_path_success() {
|
||||||
|
let input_path = PathBuf::from("tmp/blub");
|
||||||
|
let user_id = "bla";
|
||||||
|
assert_eq!(
|
||||||
|
build_system_path(&PathBuf::from("/tmp/bla"), user_id, &input_path).unwrap(),
|
||||||
|
PathBuf::from("/tmp/bla/bla/tmp/blub")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_build_system_path_error_with_absolute_user_path_returns_error() {
|
||||||
|
let input_path = PathBuf::from("tmp/blub");
|
||||||
|
let user_id = "bla";
|
||||||
|
assert_eq!(
|
||||||
|
build_system_path(&PathBuf::from("/tmp/bla"), user_id, &input_path),
|
||||||
|
Err(
|
||||||
|
ProblemDetails::from_status_code(StatusCode::BAD_REQUEST).with_detail(
|
||||||
|
errors::ERROR_DETAILS_ABSOLUTE_PATH_NOT_ALLOWED.to_owned()
|
||||||
|
+ format!(" Provided Path: {:?}", input_path).as_str()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
casket-backend/src/files/upload.rs
Normal file
70
casket-backend/src/files/upload.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use crate::errors::to_internal_error;
|
||||||
|
use crate::files::build_system_path;
|
||||||
|
use crate::{errors, AppState};
|
||||||
|
use axum::body::Bytes;
|
||||||
|
use axum::debug_handler;
|
||||||
|
use axum::extract::multipart::{Field, MultipartError};
|
||||||
|
use axum::extract::{Multipart, State};
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
use futures::Stream;
|
||||||
|
use futures_util::stream::MapErr;
|
||||||
|
use futures_util::{StreamExt, TryStreamExt};
|
||||||
|
use problem_details::ProblemDetails;
|
||||||
|
use std::io;
|
||||||
|
use std::io::Error;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tokio::fs::{create_dir_all, File};
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn handle_file_uploads(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
axum::extract::Path(user_id): axum::extract::Path<String>,
|
||||||
|
mut multipart: Multipart,
|
||||||
|
) -> Result<(), ProblemDetails> {
|
||||||
|
loop {
|
||||||
|
let next = multipart.next_field().await;
|
||||||
|
match next {
|
||||||
|
Err(error) => return Err(to_internal_error(error)),
|
||||||
|
Ok(Some(field)) => handle_single_file_upload(&state, &user_id, field).await?,
|
||||||
|
Ok(None) => return Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clippy behaves weird. When removing the lifetime the compilation fails since the Field type has
|
||||||
|
// a generic lifetime around the multipart.
|
||||||
|
#[allow(clippy::needless_lifetimes)]
|
||||||
|
async fn handle_single_file_upload<'field_lifetime>(
|
||||||
|
state: &AppState,
|
||||||
|
user_id: &str,
|
||||||
|
field: Field<'field_lifetime>,
|
||||||
|
) -> Result<(), ProblemDetails> {
|
||||||
|
match field.name() {
|
||||||
|
None | Some("") => Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||||
|
.with_detail(errors::ERROR_DETAILS_NO_NAME_PROVIDED_MULTIPART_FIELD)),
|
||||||
|
Some(field_name) => {
|
||||||
|
let path = PathBuf::from(field_name);
|
||||||
|
let filesystem_path = build_system_path(&state.config.files.directory, user_id, &path)?;
|
||||||
|
save_file(filesystem_path, map_error_to_io_error(field))
|
||||||
|
.await
|
||||||
|
.map_err(to_internal_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn save_file(
|
||||||
|
path: PathBuf,
|
||||||
|
mut content: impl Stream<Item = Result<Bytes, Error>> + Unpin,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
create_dir_all(path.parent().unwrap()).await?;
|
||||||
|
let mut file = File::create(path).await?;
|
||||||
|
while let Some(bytes) = content.next().await {
|
||||||
|
file.write_all(&bytes?).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_error_to_io_error(field: Field) -> MapErr<Field, fn(MultipartError) -> Error> {
|
||||||
|
field.map_err(|err| Error::new(io::ErrorKind::Other, err))
|
||||||
|
}
|
||||||
@ -1,33 +1,56 @@
|
|||||||
|
#![warn(clippy::pedantic)]
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
mod errors;
|
||||||
|
mod files;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
mod extractor_helper;
|
||||||
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
|
use std::env;
|
||||||
|
use std::process::exit;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
config: config::Config,
|
config: config::Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_max_level(get_log_level())
|
||||||
|
.init();
|
||||||
let config = config::get_config();
|
let config = config::get_config();
|
||||||
match config {
|
match config {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
println!("Loaded config: {:#?}", &config);
|
debug!("Config loaded {:?}", &config,);
|
||||||
// build our application with a route
|
let bind = format!("{}:{}", &config.server.bind_address, &config.server.port);
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.merge(routes::routes())
|
.merge(routes::routes())
|
||||||
.with_state(AppState { config });
|
.with_state(AppState { config });
|
||||||
|
|
||||||
// run it
|
let listener = tokio::net::TcpListener::bind(bind).await.unwrap();
|
||||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
info!("listening on {}", listener.local_addr().unwrap());
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
println!("listening on {}", listener.local_addr().unwrap());
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
println!("Error while loading config: {}", error);
|
error!(
|
||||||
|
"Error while loading config: {}. Tried loading from the following files {:?} and env variables prefixed by CASKET_.",
|
||||||
|
error,
|
||||||
|
config::CONFIG_LOCATIONS
|
||||||
|
);
|
||||||
|
exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_log_level() -> tracing::Level {
|
||||||
|
let env = env::var("CASKET_LOG_LEVEL");
|
||||||
|
match env {
|
||||||
|
Ok(value) => tracing::Level::from_str(&value)
|
||||||
|
.unwrap_or_else(|_| panic!("Failed to parse log level env {value}")),
|
||||||
|
Err(_) => tracing::Level::INFO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
16
casket-backend/src/routes.rs
Normal file
16
casket-backend/src/routes.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use crate::files;
|
||||||
|
use crate::AppState;
|
||||||
|
use axum::routing::post;
|
||||||
|
use axum::{response::Html, routing::get, Router};
|
||||||
|
use files::list::query_file_tree;
|
||||||
|
|
||||||
|
pub fn routes() -> Router<AppState> {
|
||||||
|
Router::new().route("/", get(handler)).route(
|
||||||
|
"/api/v1/user/{:user_id}/files",
|
||||||
|
post(files::upload::handle_file_uploads).get(query_file_tree),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handler() -> Html<&'static str> {
|
||||||
|
Html("<h1>Hello, World!</h1>")
|
||||||
|
}
|
||||||
@ -1,11 +0,0 @@
|
|||||||
use axum::{response::Html, routing::get, Router};
|
|
||||||
|
|
||||||
use crate::AppState;
|
|
||||||
|
|
||||||
pub fn routes() -> Router<AppState> {
|
|
||||||
Router::new().route("/", get(handler))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handler() -> Html<&'static str> {
|
|
||||||
Html("<h1>Hello, World!</h1>")
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user