Add image API
- Add is_admin field to User model. - Add functions to get and validate current user. - Improve error handling. - Add routes to upload, view and delete images.
This commit is contained in:
parent
f65ae78eb9
commit
559d6a7d9e
14 changed files with 871 additions and 59 deletions
583
Cargo.lock
generated
583
Cargo.lock
generated
|
@ -38,6 +38,12 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned-vec"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
|
@ -114,12 +120,35 @@ version = "1.0.91"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775a8770d29db3dadcb858482cc240af7b2ffde4ac4de67d1d4955728103f0e2"
|
||||
|
||||
[[package]]
|
||||
name = "arg_enum_proc_macro"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arraydeque"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.83"
|
||||
|
@ -152,6 +181,29 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "av1-grain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
"log",
|
||||
"nom",
|
||||
"num-rational",
|
||||
"v_frame",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "avif-serialize"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.7.7"
|
||||
|
@ -172,6 +224,7 @@ dependencies = [
|
|||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"multer",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
|
@ -275,6 +328,18 @@ version = "1.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
|
@ -284,6 +349,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitstream-io"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
|
@ -303,18 +374,36 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.8.0"
|
||||
|
@ -327,9 +416,21 @@ version = "1.1.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -372,6 +473,12 @@ dependencies = [
|
|||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
|
@ -389,6 +496,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"config",
|
||||
"env_logger",
|
||||
"image",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
"log",
|
||||
|
@ -402,6 +510,7 @@ dependencies = [
|
|||
"tower",
|
||||
"tower-http",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -518,6 +627,15 @@ version = "2.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
|
@ -700,12 +818,46 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.73.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
|
@ -842,6 +994,16 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
|
@ -867,7 +1029,7 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
|
@ -891,6 +1053,16 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
@ -1159,6 +1331,45 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"image-webp",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"ravif",
|
||||
"rayon",
|
||||
"rgb",
|
||||
"tiff",
|
||||
"zune-core",
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image-webp"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
|
||||
dependencies = [
|
||||
"byteorder-lite",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgref"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
|
@ -1169,6 +1380,17 @@ dependencies = [
|
|||
"hashbrown 0.15.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interpolate_name"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.10.1"
|
||||
|
@ -1181,12 +1403,36 @@ version = "1.70.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
|
@ -1231,12 +1477,29 @@ dependencies = [
|
|||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
|
@ -1276,12 +1539,31 @@ version = "0.4.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "loop9"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
|
||||
dependencies = [
|
||||
"imgref",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-rayon"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
|
@ -1327,6 +1609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1341,6 +1624,23 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[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 = "native-tls"
|
||||
version = "0.2.12"
|
||||
|
@ -1358,6 +1658,12 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
|
@ -1368,6 +1674,12 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "noop_proc_macro"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
|
@ -1401,6 +1713,17 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
|
@ -1421,6 +1744,17 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
|
@ -1452,7 +1786,7 @@ version = "0.10.68"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
@ -1697,6 +2031,19 @@ version = "0.3.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
|
@ -1721,6 +2068,40 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
|
||||
dependencies = [
|
||||
"profiling-procmacros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling-procmacros"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
|
@ -1760,13 +2141,83 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rav1e"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"arg_enum_proc_macro",
|
||||
"arrayvec",
|
||||
"av1-grain",
|
||||
"bitstream-io",
|
||||
"built",
|
||||
"cfg-if",
|
||||
"interpolate_name",
|
||||
"itertools",
|
||||
"libc",
|
||||
"libfuzzer-sys",
|
||||
"log",
|
||||
"maybe-rayon",
|
||||
"new_debug_unreachable",
|
||||
"noop_proc_macro",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"profiling",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"simd_helpers",
|
||||
"system-deps",
|
||||
"thiserror",
|
||||
"v_frame",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ravif"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6"
|
||||
dependencies = [
|
||||
"avif-serialize",
|
||||
"imgref",
|
||||
"loop9",
|
||||
"quick-error",
|
||||
"rav1e",
|
||||
"rayon",
|
||||
"rgb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1841,6 +2292,12 @@ dependencies = [
|
|||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
|
@ -1863,7 +2320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -1910,7 +2367,7 @@ version = "0.38.38"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
|
@ -1998,7 +2455,7 @@ version = "2.11.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
@ -2125,6 +2582,21 @@ dependencies = [
|
|||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "simd_helpers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.6.2"
|
||||
|
@ -2261,6 +2733,7 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2310,7 +2783,7 @@ checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a"
|
|||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
|
@ -2342,6 +2815,7 @@ dependencies = [
|
|||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
@ -2353,7 +2827,7 @@ checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8"
|
|||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
|
@ -2381,6 +2855,7 @@ dependencies = [
|
|||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
@ -2406,6 +2881,7 @@ dependencies = [
|
|||
"sqlx-core",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2457,7 +2933,7 @@ version = "0.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
@ -2472,6 +2948,25 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
|
||||
dependencies = [
|
||||
"cfg-expr",
|
||||
"heck",
|
||||
"pkg-config",
|
||||
"toml",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.13.0"
|
||||
|
@ -2527,6 +3022,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
|
@ -2712,7 +3218,7 @@ version = "0.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.6.0",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
@ -2912,12 +3418,39 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "v_frame"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b"
|
||||
dependencies = [
|
||||
"aligned-vec",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
|
@ -3032,6 +3565,12 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.5.2"
|
||||
|
@ -3284,3 +3823,27 @@ name = "zeroize"
|
|||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
|
|
@ -5,13 +5,13 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
tokio = { version = "1.41.0", features = ["full"] }
|
||||
axum = { version = "0.7.7", features = [ "macros" ] }
|
||||
axum = { version = "0.7.7", features = [ "macros", "multipart", "json" ] }
|
||||
axum-extra = { version = "0.9.4", features = [ "cookie" ] }
|
||||
tera = "1.20.0"
|
||||
lazy_static = "1.5.0"
|
||||
tower = "0.5.1"
|
||||
tower-http = { version = "0.6.1", features = ["fs"] }
|
||||
sqlx = { version = "0.8.2", features = [ "runtime-tokio", "postgres", "chrono" ] }
|
||||
sqlx = { version = "0.8.2", features = [ "runtime-tokio", "postgres", "chrono", "uuid" ] }
|
||||
url = { version = "2.5.2", features = [ "serde" ] }
|
||||
serde = { version = "1.0.214", features = [ "derive" ] }
|
||||
config = "0.14.1"
|
||||
|
@ -24,3 +24,5 @@ rand = "0.8.5"
|
|||
rand_core = { version = "0.6.4", features = [ "getrandom" ] }
|
||||
base64 = "0.22.1"
|
||||
chrono = "0.4.38"
|
||||
uuid = { version = "1.11.0", features = [ "v4", "serde" ] }
|
||||
image = "0.25.5"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use config::{Config, Environment, File};
|
||||
use lazy_static::lazy_static;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OpenIdConfig {
|
||||
|
@ -23,6 +24,7 @@ pub struct AppConfig {
|
|||
pub url: url::Url,
|
||||
/// Port for the web server.
|
||||
pub port: u16,
|
||||
pub uploads_dir: PathBuf,
|
||||
pub openid: OpenIdConfig,
|
||||
pub database: DatabaseConfig,
|
||||
}
|
||||
|
|
68
src/internal/db/images.rs
Normal file
68
src/internal/db/images.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use sqlx::PgPool;
|
||||
|
||||
pub struct NewImage {
|
||||
pub id: uuid::Uuid,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub mime_type: String,
|
||||
pub original_filename: Option<String>,
|
||||
pub alt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
pub struct Image {
|
||||
pub id: uuid::Uuid,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub mime_type: String,
|
||||
pub original_filename: String,
|
||||
pub alt: Option<String>,
|
||||
pub uploaded_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
pub async fn save_image(pool: &PgPool, img: NewImage) -> Result<(), sqlx::Error> {
|
||||
sqlx::query(
|
||||
"
|
||||
INSERT INTO images(id, width, height, mime_type, original_filename, alt)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
",
|
||||
)
|
||||
.bind(img.id)
|
||||
.bind(img.width)
|
||||
.bind(img.height)
|
||||
.bind(img.mime_type)
|
||||
.bind(img.original_filename)
|
||||
.bind(img.alt)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_image(pool: &PgPool, id: uuid::Uuid) -> Result<Image, sqlx::Error> {
|
||||
sqlx::query_as::<_, Image>(
|
||||
"
|
||||
SELECT *
|
||||
FROM images
|
||||
WHERE id = $1
|
||||
",
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_image(pool: &PgPool, id: uuid::Uuid) -> Result<(), sqlx::Error> {
|
||||
sqlx::query_as::<_, (i32,)>(
|
||||
"
|
||||
DELETE FROM images
|
||||
WHERE id = $1
|
||||
RETURNING 1
|
||||
",
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
12
src/internal/db/migrations/0002_images.sql
Normal file
12
src/internal/db/migrations/0002_images.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE images(
|
||||
id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
width INTEGER NOT NULL,
|
||||
height INTEGER NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
original_filename TEXT,
|
||||
alt TEXT,
|
||||
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
ALTER TABLE users
|
||||
ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT FALSE;
|
|
@ -1,3 +1,4 @@
|
|||
pub mod images;
|
||||
pub mod users;
|
||||
|
||||
pub async fn apply_migrations(pool: &sqlx::PgPool) -> Result<(), sqlx::migrate::MigrateError> {
|
||||
|
|
|
@ -19,6 +19,7 @@ pub struct User {
|
|||
pub username: String,
|
||||
pub email: String,
|
||||
pub display_name: String,
|
||||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
/// Create a new user using external auth provider.
|
||||
|
@ -162,9 +163,8 @@ pub async fn generate_openid_state(pool: &PgPool) -> Result<[u8; 16], anyhow::Er
|
|||
}
|
||||
|
||||
/// Check state returned by openid authorization endpoint.
|
||||
/// Returns true if provided state was found and deleted.
|
||||
pub async fn delete_openid_state(pool: &PgPool, state: Vec<u8>) -> Result<bool, anyhow::Error> {
|
||||
let res = sqlx::query_as::<_, (i32,)>(
|
||||
pub async fn delete_openid_state(pool: &PgPool, state: Vec<u8>) -> Result<(), anyhow::Error> {
|
||||
sqlx::query_as::<_, (i32,)>(
|
||||
"
|
||||
DELETE FROM tokens
|
||||
WHERE context = $1
|
||||
|
@ -174,8 +174,8 @@ pub async fn delete_openid_state(pool: &PgPool, state: Vec<u8>) -> Result<bool,
|
|||
)
|
||||
.bind(TOKEN_CONTEXT_OPENID)
|
||||
.bind(state)
|
||||
.fetch_optional(pool)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(res.is_some())
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -37,6 +37,11 @@ async fn main() {
|
|||
.route("/openid/signin", post(controllers::signin))
|
||||
.route("/openid/exchange-code", get(controllers::exchange_code))
|
||||
.route("/logout", post(controllers::logout))
|
||||
.route("/api/images", post(controllers::upload_image))
|
||||
.route(
|
||||
"/api/images/:image_id",
|
||||
get(controllers::get_image).delete(controllers::delete_image),
|
||||
)
|
||||
.nest_service("/assets", ServeDir::new("src/web/assets"))
|
||||
.with_state(shared_state.clone());
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
use axum::{
|
||||
extract::State,
|
||||
http::header::SET_COOKIE,
|
||||
http::{header::SET_COOKIE, StatusCode},
|
||||
response::{AppendHeaders, IntoResponse, Redirect},
|
||||
};
|
||||
use axum_extra::extract::CookieJar;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::ControllerError;
|
||||
use crate::internal::db::users;
|
||||
|
||||
pub async fn logout(
|
||||
cookies: CookieJar,
|
||||
State(state): State<Arc<super::AppState>>,
|
||||
) -> Result<impl IntoResponse, super::ControllerError> {
|
||||
) -> Result<impl IntoResponse, ControllerError> {
|
||||
let token = super::ctx::extract_auth_token(cookies)?;
|
||||
|
||||
match token {
|
||||
|
@ -21,6 +22,10 @@ pub async fn logout(
|
|||
let headers = AppendHeaders([(SET_COOKIE, "comfy-session=deleted; Max-Age=0;")]);
|
||||
Ok((headers, Redirect::to("/")))
|
||||
}
|
||||
None => Err(anyhow::anyhow!("can't find session token").into()),
|
||||
None => Err(ControllerError {
|
||||
status_code: StatusCode::UNAUTHORIZED,
|
||||
description: String::from("Can't find session token. Are you logged in?"),
|
||||
details: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
use anyhow::Result;
|
||||
use axum::http::StatusCode;
|
||||
use axum_extra::extract::cookie::CookieJar;
|
||||
use base64::prelude::*;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::internal::db::users;
|
||||
use super::ControllerError;
|
||||
use crate::internal::db::users::{self, User};
|
||||
|
||||
pub fn extract_auth_token(cookies: CookieJar) -> Result<Option<Vec<u8>>, anyhow::Error> {
|
||||
pub fn extract_auth_token(cookies: CookieJar) -> Result<Option<Vec<u8>>> {
|
||||
let cookie = match cookies.get("comfy-session") {
|
||||
Some(cookie) => cookie,
|
||||
None => {
|
||||
|
@ -15,10 +19,7 @@ pub fn extract_auth_token(cookies: CookieJar) -> Result<Option<Vec<u8>>, anyhow:
|
|||
}
|
||||
|
||||
/// Get current user by looking at request cookies.
|
||||
pub async fn get_user(
|
||||
pool: &sqlx::PgPool,
|
||||
cookies: CookieJar,
|
||||
) -> Result<Option<users::User>, anyhow::Error> {
|
||||
pub async fn get_optional_user(pool: &PgPool, cookies: CookieJar) -> Result<Option<User>> {
|
||||
let token = extract_auth_token(cookies)?;
|
||||
match token {
|
||||
Some(token) => {
|
||||
|
@ -29,12 +30,37 @@ pub async fn get_user(
|
|||
}
|
||||
}
|
||||
|
||||
/// Get context for templates with user field set.
|
||||
pub async fn get_context(
|
||||
pool: &sqlx::PgPool,
|
||||
cookies: CookieJar,
|
||||
) -> Result<tera::Context, anyhow::Error> {
|
||||
/// Get current user by looking at request headers.
|
||||
/// Returns an error if the user is not authorized.
|
||||
pub async fn get_user(pool: &PgPool, cookies: CookieJar) -> Result<User, ControllerError> {
|
||||
match get_optional_user(pool, cookies).await? {
|
||||
Some(user) => Ok(user),
|
||||
None => Err(ControllerError {
|
||||
status_code: StatusCode::UNAUTHORIZED,
|
||||
description: String::from("You need to log in to your account to access this page."),
|
||||
details: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current user by looking at request headers.
|
||||
/// Returns an error if the user is not authorized or is not an administrator..
|
||||
pub async fn get_admin_user(pool: &PgPool, cookies: CookieJar) -> Result<User, ControllerError> {
|
||||
let user = get_user(pool, cookies).await?;
|
||||
if !user.is_admin {
|
||||
return Err(ControllerError {
|
||||
status_code: StatusCode::FORBIDDEN,
|
||||
description: String::from("Only administrators can access this page."),
|
||||
details: None,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
/// Get context for templates with user field set.
|
||||
pub async fn get_context(pool: &sqlx::PgPool, cookies: CookieJar) -> Result<tera::Context> {
|
||||
let user = get_optional_user(pool, cookies).await?;
|
||||
let mut ctx = tera::Context::new();
|
||||
ctx.insert("user", &user);
|
||||
Ok(ctx)
|
||||
|
|
|
@ -4,26 +4,36 @@ use axum::{
|
|||
};
|
||||
use log::error;
|
||||
|
||||
pub struct ControllerError(anyhow::Error);
|
||||
pub struct ControllerError {
|
||||
pub status_code: StatusCode,
|
||||
/// Description of the error available to the user.
|
||||
pub description: String,
|
||||
/// Detailed description of the error that is not sent to the user.
|
||||
pub details: Option<String>,
|
||||
}
|
||||
|
||||
impl IntoResponse for ControllerError {
|
||||
fn into_response(self) -> Response {
|
||||
error!("{}", self.0);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"something went wrong, see logs for details",
|
||||
)
|
||||
.into_response()
|
||||
if let Some(details) = self.details {
|
||||
error!("{}", details);
|
||||
}
|
||||
|
||||
(self.status_code, self.description).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into `Result<_, AppError>`.
|
||||
// That way you don't need to do that manually.
|
||||
// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into `Result<_, ControllerError>`.
|
||||
impl<E> From<E> for ControllerError
|
||||
where
|
||||
E: Into<anyhow::Error>,
|
||||
E: Into<anyhow::Error> + ToString,
|
||||
{
|
||||
fn from(err: E) -> Self {
|
||||
Self(err.into())
|
||||
Self {
|
||||
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
description: String::from(
|
||||
"Something went wrong, more information is available in the logs.",
|
||||
),
|
||||
details: Some(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
115
src/web/controllers/images.rs
Normal file
115
src/web/controllers/images.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
use axum::{
|
||||
extract::{Multipart, Path, Request, State},
|
||||
http::header,
|
||||
response::IntoResponse,
|
||||
Json,
|
||||
};
|
||||
use axum_extra::extract::cookie::CookieJar;
|
||||
use image::ImageReader;
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{Cursor, Write},
|
||||
path,
|
||||
sync::Arc,
|
||||
};
|
||||
use tower_http::services::ServeFile;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{AppState, ControllerError};
|
||||
use crate::config::CONFIG;
|
||||
use crate::internal::db::images;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UploadResult {
|
||||
ids: Vec<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DeletionResult {
|
||||
id: Uuid,
|
||||
}
|
||||
|
||||
fn get_image_path(id: Uuid) -> path::PathBuf {
|
||||
CONFIG.uploads_dir.join(format!("images/{}", id.simple()))
|
||||
}
|
||||
|
||||
pub async fn upload_image(
|
||||
State(state): State<Arc<AppState>>,
|
||||
cookies: CookieJar,
|
||||
mut multipart: Multipart,
|
||||
) -> Result<Json<UploadResult>, ControllerError> {
|
||||
super::get_admin_user(&state.db, cookies).await?;
|
||||
|
||||
let mut res = UploadResult { ids: Vec::new() };
|
||||
|
||||
while let Some(field) = multipart.next_field().await? {
|
||||
let name = field.name();
|
||||
if name.is_none() || name != Some("image") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let id = Uuid::new_v4();
|
||||
let file_path = get_image_path(id);
|
||||
|
||||
let original_filename = field.file_name().map(|s| s.to_string());
|
||||
|
||||
let data = field.bytes().await?;
|
||||
let img_reader = ImageReader::new(Cursor::new(&data)).with_guessed_format()?;
|
||||
|
||||
let format = match img_reader.format() {
|
||||
Some(format) => format,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let mime_type = format.to_mime_type().to_string();
|
||||
|
||||
let decoded_image = img_reader.decode()?;
|
||||
let width: i32 = decoded_image.width().try_into().unwrap();
|
||||
let height: i32 = decoded_image.height().try_into().unwrap();
|
||||
|
||||
let mut file_handle = File::create(file_path)?;
|
||||
file_handle.write_all(&data)?;
|
||||
|
||||
let img = images::NewImage {
|
||||
id,
|
||||
width,
|
||||
height,
|
||||
mime_type,
|
||||
original_filename,
|
||||
alt: None,
|
||||
};
|
||||
images::save_image(&state.db, img).await?;
|
||||
|
||||
res.ids.push(id);
|
||||
}
|
||||
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
pub async fn get_image(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(image_id): Path<Uuid>,
|
||||
req: Request,
|
||||
) -> Result<impl IntoResponse, ControllerError> {
|
||||
let image_path = get_image_path(image_id);
|
||||
let db_img = images::get_image(&state.db, image_id).await?;
|
||||
let content = ServeFile::new(image_path).try_call(req).await?;
|
||||
|
||||
Ok(([(header::CONTENT_TYPE, db_img.mime_type)], content))
|
||||
}
|
||||
|
||||
pub async fn delete_image(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(image_id): Path<Uuid>,
|
||||
cookies: CookieJar,
|
||||
) -> Result<Json<DeletionResult>, ControllerError> {
|
||||
super::get_admin_user(&state.db, cookies).await?;
|
||||
|
||||
images::delete_image(&state.db, image_id).await?;
|
||||
|
||||
let image_path = get_image_path(image_id);
|
||||
fs::remove_file(image_path)?;
|
||||
|
||||
Ok(Json(DeletionResult { id: image_id }))
|
||||
}
|
|
@ -2,10 +2,12 @@ mod auth;
|
|||
mod ctx;
|
||||
mod error;
|
||||
mod home;
|
||||
mod images;
|
||||
mod openid;
|
||||
|
||||
pub use auth::*;
|
||||
pub use home::*;
|
||||
pub use images::{delete_image, get_image, upload_image};
|
||||
pub use openid::*;
|
||||
|
||||
use ctx::*;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use axum::{
|
||||
extract::{Query, State},
|
||||
http::header::SET_COOKIE,
|
||||
http::{header::SET_COOKIE, StatusCode},
|
||||
response::{AppendHeaders, IntoResponse, Redirect},
|
||||
};
|
||||
use base64::prelude::*;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use url::Url;
|
||||
|
||||
use super::{AppState, ControllerError};
|
||||
use crate::config::CONFIG;
|
||||
use crate::internal::db::users;
|
||||
use crate::internal::openid;
|
||||
|
@ -18,9 +19,7 @@ fn gen_redirect_uri() -> String {
|
|||
}
|
||||
|
||||
/// Redirect the user to the authorization page.
|
||||
pub async fn signin(
|
||||
State(state): State<Arc<super::AppState>>,
|
||||
) -> Result<Redirect, super::ControllerError> {
|
||||
pub async fn signin(State(state): State<Arc<AppState>>) -> Result<Redirect, ControllerError> {
|
||||
let raw_openid_state = users::generate_openid_state(&state.db).await?;
|
||||
let openid_state = BASE64_URL_SAFE.encode(raw_openid_state);
|
||||
|
||||
|
@ -44,20 +43,22 @@ pub async fn signin(
|
|||
|
||||
pub async fn exchange_code(
|
||||
Query(params): Query<HashMap<String, String>>,
|
||||
State(state): State<Arc<super::AppState>>,
|
||||
) -> Result<impl IntoResponse, super::ControllerError> {
|
||||
let openid_state = params
|
||||
.get("state")
|
||||
.ok_or(anyhow::anyhow!("state is missing"))?;
|
||||
let raw_openid_state = BASE64_URL_SAFE.decode(openid_state)?;
|
||||
let state_found = users::delete_openid_state(&state.db, raw_openid_state).await?;
|
||||
if !state_found {
|
||||
return Err(anyhow::anyhow!("state was not found").into());
|
||||
}
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<impl IntoResponse, ControllerError> {
|
||||
let openid_state = params.get("state").ok_or(ControllerError {
|
||||
status_code: StatusCode::BAD_REQUEST,
|
||||
description: String::from("Parameter 'state' was not found."),
|
||||
details: None,
|
||||
})?;
|
||||
let code = params.get("code").ok_or(ControllerError {
|
||||
status_code: StatusCode::BAD_REQUEST,
|
||||
description: String::from("Parameter 'code' was not found."),
|
||||
details: None,
|
||||
})?;
|
||||
|
||||
let raw_openid_state = BASE64_URL_SAFE.decode(openid_state)?;
|
||||
users::delete_openid_state(&state.db, raw_openid_state).await?;
|
||||
|
||||
let code = params
|
||||
.get("code")
|
||||
.ok_or(anyhow::anyhow!("authorization code is missing"))?;
|
||||
let creds = openid::exchange_code(code, &gen_redirect_uri()).await?;
|
||||
let token = openid::parse_id_token(creds.id_token).await?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue