EXTREMELY EVIL CACKLING
This commit is contained in:
parent
462134b36d
commit
f64e309425
581
Cargo.lock
generated
581
Cargo.lock
generated
|
@ -2,6 +2,18 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler32"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
|
@ -25,6 +37,12 @@ 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 = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
|
checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
|
@ -40,6 +58,12 @@ dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
|
@ -80,6 +104,12 @@ dependencies = [
|
||||||
"safemem",
|
"safemem",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecheck"
|
name = "bytecheck"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
|
@ -101,6 +131,27 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -113,6 +164,66 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"memoffset",
|
||||||
|
"once_cell",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crow"
|
name = "crow"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -120,11 +231,15 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64-url",
|
"base64-url",
|
||||||
"blake2",
|
"blake2",
|
||||||
|
"image",
|
||||||
"lmdb-zero",
|
"lmdb-zero",
|
||||||
|
"matchit",
|
||||||
"multipart",
|
"multipart",
|
||||||
"rkyv",
|
"rkyv",
|
||||||
|
"thiserror",
|
||||||
"tiny_http",
|
"tiny_http",
|
||||||
"url",
|
"url",
|
||||||
|
"webp",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -137,6 +252,15 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
|
||||||
|
dependencies = [
|
||||||
|
"adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
|
@ -148,6 +272,28 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "exr"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14cc0e06fb5f67e5d6beadf3a382fec9baca1aa751c6d5368fdeee7e5932c215"
|
||||||
|
dependencies = [
|
||||||
|
"bit_field",
|
||||||
|
"deflate",
|
||||||
|
"flume",
|
||||||
|
"half",
|
||||||
|
"inflate",
|
||||||
|
"lebe",
|
||||||
|
"smallvec",
|
||||||
|
"threadpool",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -157,6 +303,29 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flume"
|
||||||
|
version = "0.10.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ceeb589a3157cac0ab8cc585feb749bd2cea5cb55a6ee802ad72d9fd38303da"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"nanorand",
|
||||||
|
"pin-project",
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -167,6 +336,18 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gcc"
|
name = "gcc"
|
||||||
version = "0.3.55"
|
version = "0.3.55"
|
||||||
|
@ -190,10 +371,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
|
||||||
|
dependencies = [
|
||||||
|
"color_quant",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -203,6 +402,15 @@ dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.7.1"
|
version = "1.7.1"
|
||||||
|
@ -220,6 +428,35 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.24.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28edd9d7bc256be2502e325ac0628bde30b7001b9b52e0abe31a1a9dc2701212"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"exr",
|
||||||
|
"gif",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
"scoped_threadpool",
|
||||||
|
"tiff",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inflate"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
|
||||||
|
dependencies = [
|
||||||
|
"adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
@ -235,6 +472,45 @@ 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 = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jpeg-decoder"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b"
|
||||||
|
dependencies = [
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lebe"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.126"
|
||||||
|
@ -251,6 +527,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libwebp-sys"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439fd1885aa28937e7edcd68d2e793cb4a22f8733460d2519fbafd2b215672bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lmdb-zero"
|
name = "lmdb-zero"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
@ -263,6 +548,16 @@ dependencies = [
|
||||||
"supercow",
|
"supercow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.17"
|
||||||
|
@ -278,12 +573,27 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchit"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3dfc802da7b1cf80aefffa0c7b2f77247c8b32206cc83c270b61264f5b360a80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
version = "0.3.16"
|
version = "0.3.16"
|
||||||
|
@ -300,10 +610,19 @@ dependencies = [
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multipart"
|
name = "multipart"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/emily-signet/multipart#8144529477faf92941206320395fc3798c14fdf7"
|
source = "git+https://github.com/emily-signet/multipart#3ae1cd313be14b94997405fef38e29a2edb60232"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"buf_redux",
|
"buf_redux",
|
||||||
"httparse",
|
"httparse",
|
||||||
|
@ -318,6 +637,66 @@ dependencies = [
|
||||||
"twoway",
|
"twoway",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nanorand"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_threads"
|
name = "num_threads"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -339,6 +718,38 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-internal"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"crc32fast",
|
||||||
|
"deflate",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -419,6 +830,30 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
@ -477,12 +912,39 @@ version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped_threadpool"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seahash"
|
name = "seahash"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
|
@ -520,6 +982,46 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "threadpool"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
|
||||||
|
dependencies = [
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiff"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cfada0986f446a770eca461e8c6566cb879682f7d687c8348aa0c857bd52286"
|
||||||
|
dependencies = [
|
||||||
|
"flate2",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -541,8 +1043,7 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny_http"
|
name = "tiny_http"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/emily-signet/tiny-http.git#6228d7b6559ef13f6a1a3f46f6f2cf0b306a386a"
|
||||||
checksum = "e0d6ef4e10d23c1efb862eecad25c5054429a71958b4eeef85eb5e7170b477ca"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ascii",
|
"ascii",
|
||||||
"chunked_transfer",
|
"chunked_transfer",
|
||||||
|
@ -604,9 +1105,9 @@ checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.20"
|
version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd"
|
checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
@ -635,6 +1136,76 @@ 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 = "wasm-bindgen"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webp"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf022f821f166079a407d000ab57e84de020e66ffbbf4edde999bc7d6e371cae"
|
||||||
|
dependencies = [
|
||||||
|
"image",
|
||||||
|
"libwebp-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -9,8 +9,12 @@ edition = "2021"
|
||||||
anyhow = "1.0.58"
|
anyhow = "1.0.58"
|
||||||
base64-url = "1.4.13"
|
base64-url = "1.4.13"
|
||||||
blake2 = "0.10.4"
|
blake2 = "0.10.4"
|
||||||
|
image = "0.24.2"
|
||||||
lmdb-zero = "0.4.4"
|
lmdb-zero = "0.4.4"
|
||||||
|
matchit = "0.6.0"
|
||||||
multipart = { git = "https://github.com/emily-signet/multipart", default-features = false, features = ["server", "tiny_http"] }
|
multipart = { git = "https://github.com/emily-signet/multipart", default-features = false, features = ["server", "tiny_http"] }
|
||||||
rkyv = { version = "0.7.39", features = ["strict", "archive_le"] }
|
rkyv = { version = "0.7.39", features = ["strict", "archive_le"] }
|
||||||
tiny_http = "0.11.0"
|
thiserror = "1.0.31"
|
||||||
|
tiny_http = { git = "https://github.com/emily-signet/tiny-http.git" }
|
||||||
url = "2.2.2"
|
url = "2.2.2"
|
||||||
|
webp = "0.2.2"
|
||||||
|
|
BIN
db/data.mdb
Normal file
BIN
db/data.mdb
Normal file
Binary file not shown.
BIN
db/lock.mdb
Normal file
BIN
db/lock.mdb
Normal file
Binary file not shown.
237
src/lib.rs
Normal file
237
src/lib.rs
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
use blake2::Digest;
|
||||||
|
use lmdb::{traits::FromLmdbBytes, Database, Environment};
|
||||||
|
use lmdb_zero as lmdb;
|
||||||
|
|
||||||
|
use tiny_http::Request;
|
||||||
|
|
||||||
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub trait Handler: Send + Sync {
|
||||||
|
fn respond<'u>(
|
||||||
|
&self,
|
||||||
|
req: Request,
|
||||||
|
db: &DatabaseContext,
|
||||||
|
params: matchit::Params<'u, 'u>,
|
||||||
|
) -> CrowResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod routes;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum CrowError {
|
||||||
|
#[error(transparent)]
|
||||||
|
LmdbError(#[from] lmdb::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
IOError(#[from] std::io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
ImageError(#[from] image::error::ImageError),
|
||||||
|
#[error(transparent)]
|
||||||
|
Other(#[from] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CrowResult<T> = Result<T, CrowError>;
|
||||||
|
|
||||||
|
#[derive(rkyv::Archive, rkyv::Serialize)]
|
||||||
|
pub struct FileHeader {
|
||||||
|
file_name: Option<String>,
|
||||||
|
content_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLmdbBytes for ArchivedFileHeader {
|
||||||
|
fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> {
|
||||||
|
Ok(unsafe { rkyv::archived_root::<FileHeader>(bytes) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(rkyv::Serialize, rkyv::Archive, Copy, Debug, Clone)]
|
||||||
|
#[archive(compare(PartialEq))]
|
||||||
|
#[archive_attr(derive(Copy, Clone))]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum ImageFormat {
|
||||||
|
PNG,
|
||||||
|
WEBP,
|
||||||
|
JPG,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ImageFormat {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(match s {
|
||||||
|
"png" | "PNG" => ImageFormat::PNG,
|
||||||
|
"webp" | "WEBP" => ImageFormat::WEBP,
|
||||||
|
"jpg" | "JPG" | "jpeg" | "JPEG" => ImageFormat::JPG,
|
||||||
|
_ => return Err(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageFormat {
|
||||||
|
pub const fn to_mime(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
ImageFormat::PNG => "image/png",
|
||||||
|
ImageFormat::JPG => "image/jpeg",
|
||||||
|
ImageFormat::WEBP => "image/webp",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ImageFormat> for image::ImageFormat {
|
||||||
|
fn from(val: ImageFormat) -> Self {
|
||||||
|
match val {
|
||||||
|
ImageFormat::PNG => image::ImageFormat::Png,
|
||||||
|
ImageFormat::WEBP => image::ImageFormat::WebP,
|
||||||
|
ImageFormat::JPG => image::ImageFormat::Jpeg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ArchivedImageFormat> for image::ImageFormat {
|
||||||
|
fn from(val: ArchivedImageFormat) -> Self {
|
||||||
|
match val {
|
||||||
|
ArchivedImageFormat::PNG => image::ImageFormat::Png,
|
||||||
|
ArchivedImageFormat::WEBP => image::ImageFormat::WebP,
|
||||||
|
ArchivedImageFormat::JPG => image::ImageFormat::Jpeg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(rkyv::Serialize, rkyv::Archive)]
|
||||||
|
pub struct ImageHeader {
|
||||||
|
store_format: ImageFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromLmdbBytes for ArchivedImageHeader {
|
||||||
|
fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> {
|
||||||
|
Ok(unsafe { rkyv::archived_root::<ImageHeader>(bytes) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DatabaseContext {
|
||||||
|
pub env: Arc<Environment>,
|
||||||
|
pub metadata_store: Arc<Database<'static>>,
|
||||||
|
pub binary_store: Arc<Database<'static>>,
|
||||||
|
pub image_store: Arc<Database<'static>>,
|
||||||
|
pub image_meta_store: Arc<Database<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DatabaseContext {
|
||||||
|
pub fn create(path: &str) -> CrowResult<DatabaseContext> {
|
||||||
|
let env = Arc::new(unsafe {
|
||||||
|
let mut builder = lmdb::EnvBuilder::new()?;
|
||||||
|
builder.set_maxdbs(4)?;
|
||||||
|
builder.open(path, lmdb::open::NOTLS, 0o600)?
|
||||||
|
});
|
||||||
|
|
||||||
|
let binary_store = Arc::new(lmdb::Database::open(
|
||||||
|
env.clone(),
|
||||||
|
Some("binary"),
|
||||||
|
&lmdb::DatabaseOptions::new(lmdb::db::CREATE),
|
||||||
|
)?);
|
||||||
|
let image_store = Arc::new(lmdb::Database::open(
|
||||||
|
env.clone(),
|
||||||
|
Some("image"),
|
||||||
|
&lmdb::DatabaseOptions::new(lmdb::db::CREATE),
|
||||||
|
)?);
|
||||||
|
let metadata_store = Arc::new(lmdb::Database::open(
|
||||||
|
env.clone(),
|
||||||
|
Some("metadata"),
|
||||||
|
&lmdb::DatabaseOptions::new(lmdb::db::CREATE),
|
||||||
|
)?);
|
||||||
|
let image_meta_store = Arc::new(lmdb::Database::open(
|
||||||
|
env.clone(),
|
||||||
|
Some("metadata-image"),
|
||||||
|
&lmdb::DatabaseOptions::new(lmdb::db::CREATE),
|
||||||
|
)?);
|
||||||
|
|
||||||
|
Ok(DatabaseContext {
|
||||||
|
env,
|
||||||
|
binary_store,
|
||||||
|
metadata_store,
|
||||||
|
image_meta_store,
|
||||||
|
image_store,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn write_txn(&self) -> CrowResult<lmdb::WriteTransaction<'static>> {
|
||||||
|
lmdb::WriteTransaction::new(self.env.clone()).map_err(CrowError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_txn(&self) -> CrowResult<lmdb::ReadTransaction<'static>> {
|
||||||
|
lmdb::ReadTransaction::new(self.env.clone()).map_err(CrowError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod macros {
|
||||||
|
macro_rules! response {
|
||||||
|
(err $status:literal $msg:literal) => {
|
||||||
|
Response::new(
|
||||||
|
tiny_http::StatusCode($status),
|
||||||
|
vec![],
|
||||||
|
$msg.as_bytes(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! single_multipart {
|
||||||
|
($req:expr) => {
|
||||||
|
Multipart::from_request($req)
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.into_entry().into_result().ok())
|
||||||
|
.flatten()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! fn_to_handler {
|
||||||
|
($f:ident : $handler_name:ident) => {
|
||||||
|
pub struct $handler_name;
|
||||||
|
|
||||||
|
impl Handler for $handler_name {
|
||||||
|
fn respond<'u>(
|
||||||
|
&self,
|
||||||
|
req: Request,
|
||||||
|
db: &DatabaseContext,
|
||||||
|
params: matchit::Params<'u, 'u>,
|
||||||
|
) -> CrowResult<()> {
|
||||||
|
$f(req, db, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! parse_base64_hash {
|
||||||
|
($fr:expr) => {
|
||||||
|
$fr.and_then(|s| {
|
||||||
|
let mut out: [u8; 32] = [0; 32];
|
||||||
|
base64_url::decode_to_slice(s, &mut out).ok()?;
|
||||||
|
Some(out)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! some_or_response {
|
||||||
|
($opt:expr, or respond to $req:ident with $response:expr) => {
|
||||||
|
match $opt {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
$req.respond($response)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use fn_to_handler;
|
||||||
|
pub(crate) use parse_base64_hash;
|
||||||
|
pub(crate) use response;
|
||||||
|
pub(crate) use single_multipart;
|
||||||
|
pub(crate) use some_or_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use macros::*;
|
195
src/main.rs
195
src/main.rs
|
@ -1,70 +1,7 @@
|
||||||
extern crate multipart;
|
use std::{sync::Arc, thread};
|
||||||
extern crate tiny_http;
|
|
||||||
|
|
||||||
use blake2::{Blake2s256, Digest};
|
use crow::*;
|
||||||
use lmdb::{Database, Environment, LmdbResultExt};
|
use matchit::Router;
|
||||||
use lmdb_zero as lmdb;
|
|
||||||
use multipart::server::Multipart;
|
|
||||||
use rkyv::option::ArchivedOption;
|
|
||||||
use std::{io::Read, sync::Arc, thread};
|
|
||||||
use tiny_http::{Header, Request, Response};
|
|
||||||
|
|
||||||
#[derive(rkyv::Archive, rkyv::Serialize)]
|
|
||||||
pub struct FileHeader {
|
|
||||||
file_name: Option<String>,
|
|
||||||
content_type: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct DatabaseContext {
|
|
||||||
env: Arc<Environment>,
|
|
||||||
metadata_store: Arc<Database<'static>>,
|
|
||||||
binary_store: Arc<Database<'static>>,
|
|
||||||
image_store: Arc<Database<'static>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DatabaseContext {
|
|
||||||
pub fn create(path: &str) -> anyhow::Result<DatabaseContext> {
|
|
||||||
let env = Arc::new(unsafe {
|
|
||||||
let mut builder = lmdb::EnvBuilder::new()?;
|
|
||||||
builder.set_maxdbs(3)?;
|
|
||||||
builder.open(path, lmdb::open::NOTLS, 0o600)?
|
|
||||||
});
|
|
||||||
|
|
||||||
let binary_store = Arc::new(lmdb::Database::open(
|
|
||||||
env.clone(),
|
|
||||||
Some("binary"),
|
|
||||||
&lmdb::DatabaseOptions::new(lmdb::db::CREATE),
|
|
||||||
)?);
|
|
||||||
let image_store = Arc::new(lmdb::Database::open(
|
|
||||||
env.clone(),
|
|
||||||
Some("image"),
|
|
||||||
&lmdb::DatabaseOptions::new(lmdb::db::CREATE),
|
|
||||||
)?);
|
|
||||||
let metadata_store = Arc::new(lmdb::Database::open(
|
|
||||||
env.clone(),
|
|
||||||
Some("metadata"),
|
|
||||||
&lmdb::DatabaseOptions::new(lmdb::db::CREATE),
|
|
||||||
)?);
|
|
||||||
|
|
||||||
Ok(DatabaseContext {
|
|
||||||
env,
|
|
||||||
binary_store,
|
|
||||||
metadata_store,
|
|
||||||
image_store,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn write_txn(&self) -> anyhow::Result<lmdb::WriteTransaction<'static>> {
|
|
||||||
lmdb::WriteTransaction::new(self.env.clone()).map_err(anyhow::Error::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn read_txn(&self) -> anyhow::Result<lmdb::ReadTransaction<'static>> {
|
|
||||||
lmdb::ReadTransaction::new(self.env.clone()).map_err(anyhow::Error::from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let database_context = DatabaseContext::create("./db")?;
|
let database_context = DatabaseContext::create("./db")?;
|
||||||
|
@ -74,22 +11,25 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let mut guards = Vec::with_capacity(4);
|
let mut guards = Vec::with_capacity(4);
|
||||||
|
|
||||||
|
// router.insert("/upload", &routes::upload::upload_route);
|
||||||
|
// router.insert("/image", &routes::image::image_route);
|
||||||
|
|
||||||
for _ in 0..4 {
|
for _ in 0..4 {
|
||||||
let server = server.clone();
|
let server = server.clone();
|
||||||
let db = database_context.clone();
|
let db = database_context.clone();
|
||||||
|
let mut router: Router<&dyn Handler> = Router::new();
|
||||||
|
router.insert("/get/:id", &routes::get::GetHandler)?;
|
||||||
|
router.insert("/upload", &routes::upload::UploadHandler)?;
|
||||||
|
router.insert("/image/upload", &routes::image::ImageUploadHandler)?;
|
||||||
|
router.insert("/image/get/:id/:format", &routes::image::ImageGetHandler)?;
|
||||||
|
|
||||||
let guard = thread::spawn(move || loop {
|
let guard = thread::spawn(move || loop {
|
||||||
let request = server.recv().unwrap();
|
let request = server.recv().unwrap();
|
||||||
|
// i don't like this alloc
|
||||||
|
let url = request.url().to_owned();
|
||||||
|
|
||||||
match request.url() {
|
let matched = router.at(&url).unwrap();
|
||||||
"/upload" => {
|
matched.value.respond(request, &db, matched.params).unwrap();
|
||||||
process_upload(request, &db).unwrap();
|
|
||||||
}
|
|
||||||
s if s.starts_with("/get/") => {
|
|
||||||
process_get(request, &db).unwrap();
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
guards.push(guard);
|
guards.push(guard);
|
||||||
|
@ -101,108 +41,3 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_upload(mut request: Request, db_context: &DatabaseContext) -> anyhow::Result<()> {
|
|
||||||
if let Some(mut entry) = Multipart::from_request(&mut request)
|
|
||||||
.ok()
|
|
||||||
.and_then(|v| v.into_entry().into_result().ok())
|
|
||||||
.flatten()
|
|
||||||
{
|
|
||||||
let mut data: Vec<u8> = Vec::with_capacity(20000);
|
|
||||||
entry.data.read_to_end(&mut data)?;
|
|
||||||
|
|
||||||
let data_hash = Blake2s256::digest(&data);
|
|
||||||
|
|
||||||
let txn = db_context.write_txn()?;
|
|
||||||
|
|
||||||
let mut accessor = txn.access();
|
|
||||||
|
|
||||||
if accessor
|
|
||||||
.get::<[u8], [u8]>(&db_context.binary_store, data_hash.as_slice())
|
|
||||||
.to_opt()?
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
request.respond(Response::from_string(base64_url::encode(&data_hash)))?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
accessor.put(
|
|
||||||
&db_context.binary_store,
|
|
||||||
data_hash.as_slice(),
|
|
||||||
&data,
|
|
||||||
lmdb::put::Flags::empty(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let header = FileHeader {
|
|
||||||
file_name: entry.headers.filename,
|
|
||||||
content_type: entry
|
|
||||||
.headers
|
|
||||||
.content_type
|
|
||||||
.map(|v| v.essence_str().to_owned()),
|
|
||||||
};
|
|
||||||
|
|
||||||
accessor.put(
|
|
||||||
&db_context.metadata_store,
|
|
||||||
data_hash.as_slice(),
|
|
||||||
rkyv::to_bytes::<_, 256>(&header)?.as_slice(),
|
|
||||||
lmdb::put::Flags::empty(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
request.respond(Response::from_string(base64_url::encode(&data_hash)))?;
|
|
||||||
|
|
||||||
drop(accessor);
|
|
||||||
|
|
||||||
txn.commit()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_get(request: Request, db_context: &DatabaseContext) -> anyhow::Result<()> {
|
|
||||||
if let Some(get_id) = request.url().strip_prefix("/get/").and_then(|s| {
|
|
||||||
let mut out: [u8; 32] = [0; 32];
|
|
||||||
base64_url::decode_to_slice(s, &mut out).ok()?;
|
|
||||||
Some(out)
|
|
||||||
}) {
|
|
||||||
let read = db_context.read_txn()?;
|
|
||||||
let access = read.access();
|
|
||||||
|
|
||||||
if let Some(data) = access
|
|
||||||
.get::<[u8], [u8]>(&db_context.binary_store, &get_id)
|
|
||||||
.to_opt()
|
|
||||||
.unwrap()
|
|
||||||
{
|
|
||||||
let header_bytes = access
|
|
||||||
.get::<[u8], [u8]>(&db_context.metadata_store, &get_id)
|
|
||||||
.to_opt()?
|
|
||||||
.ok_or(anyhow::anyhow!("header missing for file"))?;
|
|
||||||
let header = unsafe { rkyv::archived_root::<FileHeader>(header_bytes) };
|
|
||||||
|
|
||||||
let mut response = Response::new(200.into(), vec![], data, Some(data.len()), None);
|
|
||||||
|
|
||||||
if let ArchivedOption::Some(ref file_name) = header.file_name {
|
|
||||||
response = response.with_header(
|
|
||||||
Header::from_bytes(
|
|
||||||
&b"Content-Disposition"[..],
|
|
||||||
format!("inline; filename=\"{file_name}\"").into_bytes(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ArchivedOption::Some(ref mime_type) = header.content_type {
|
|
||||||
response = response.with_header(
|
|
||||||
Header::from_bytes(&b"Content-Type"[..], mime_type.as_bytes()).unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.respond(response)?;
|
|
||||||
} else {
|
|
||||||
request.respond(Response::empty(404))?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
request.respond(Response::empty(400))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
56
src/routes/get.rs
Normal file
56
src/routes/get.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use crate::*;
|
||||||
|
use lmdb::LmdbResultExt;
|
||||||
|
use lmdb_zero as lmdb;
|
||||||
|
|
||||||
|
use rkyv::option::ArchivedOption;
|
||||||
|
|
||||||
|
use tiny_http::{Header, Request, Response};
|
||||||
|
|
||||||
|
fn get<'u>(
|
||||||
|
request: Request,
|
||||||
|
db_context: &DatabaseContext,
|
||||||
|
params: matchit::Params<'u, 'u>,
|
||||||
|
) -> CrowResult<()> {
|
||||||
|
let get_id = some_or_response!(
|
||||||
|
parse_base64_hash!(params.get("id")), or respond to request with Response::empty(400)
|
||||||
|
);
|
||||||
|
|
||||||
|
let read = db_context.read_txn()?;
|
||||||
|
let access = read.access();
|
||||||
|
|
||||||
|
let data = some_or_response!(
|
||||||
|
access
|
||||||
|
.get::<[u8], [u8]>(&db_context.binary_store, &get_id)
|
||||||
|
.to_opt()?,
|
||||||
|
or respond to request with
|
||||||
|
Response::empty(404)
|
||||||
|
);
|
||||||
|
|
||||||
|
let header = access
|
||||||
|
.get::<[u8], ArchivedFileHeader>(&db_context.metadata_store, &get_id)
|
||||||
|
.to_opt()?
|
||||||
|
.ok_or(anyhow::anyhow!("header missing for file"))?;
|
||||||
|
|
||||||
|
let mut response = Response::new(200.into(), vec![], data, Some(data.len()), None);
|
||||||
|
|
||||||
|
if let ArchivedOption::Some(ref file_name) = header.file_name {
|
||||||
|
response = response.with_header(
|
||||||
|
Header::from_bytes(
|
||||||
|
&b"Content-Disposition"[..],
|
||||||
|
format!("inline; filename=\"{file_name}\"").into_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ArchivedOption::Some(ref mime_type) = header.content_type {
|
||||||
|
response = response
|
||||||
|
.with_header(Header::from_bytes(&b"Content-Type"[..], mime_type.as_bytes()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
request.respond(response)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_to_handler!(get: GetHandler);
|
200
src/routes/image.rs
Normal file
200
src/routes/image.rs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
use crate::*;
|
||||||
|
use blake2::{Blake2s256, Digest};
|
||||||
|
use image::{
|
||||||
|
codecs::{jpeg::JpegEncoder, png::PngEncoder},
|
||||||
|
ColorType, ImageEncoder,
|
||||||
|
};
|
||||||
|
use lmdb::LmdbResultExt;
|
||||||
|
use lmdb_zero as lmdb;
|
||||||
|
use multipart::server::Multipart;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
io::{ErrorKind, Read},
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
use tiny_http::{Header, Request, Response};
|
||||||
|
|
||||||
|
fn image_upload<'u>(
|
||||||
|
mut request: Request,
|
||||||
|
db_context: &DatabaseContext,
|
||||||
|
_: matchit::Params<'u, 'u>,
|
||||||
|
) -> CrowResult<()> {
|
||||||
|
let mut entry = some_or_response!(
|
||||||
|
single_multipart!(&mut request), or respond to request with Response::empty(400)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut data: Vec<u8> = Vec::with_capacity(20000);
|
||||||
|
entry.data.read_to_end(&mut data)?;
|
||||||
|
|
||||||
|
let upload_format = image::guess_format(&data)?;
|
||||||
|
let img = image::load_from_memory_with_format(&data, upload_format)?;
|
||||||
|
|
||||||
|
let data_hash = Blake2s256::digest(&img.as_bytes());
|
||||||
|
|
||||||
|
let txn = db_context.write_txn()?;
|
||||||
|
|
||||||
|
let mut accessor = txn.access();
|
||||||
|
|
||||||
|
if accessor
|
||||||
|
.get::<[u8], [u8]>(&db_context.image_store, data_hash.as_slice())
|
||||||
|
.to_opt()?
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
request.respond(Response::from_string(base64_url::encode(&data_hash)))?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let store_format = match upload_format {
|
||||||
|
image::ImageFormat::Jpeg => {
|
||||||
|
accessor.put(
|
||||||
|
&db_context.image_store,
|
||||||
|
data_hash.as_slice(),
|
||||||
|
&data,
|
||||||
|
lmdb::put::Flags::empty(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
ImageFormat::JPG
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// TODO: allow lossy compression via option
|
||||||
|
let encoded = webp::Encoder::from_image(&img).unwrap().encode_lossless();
|
||||||
|
accessor.put(
|
||||||
|
&db_context.image_store,
|
||||||
|
data_hash.as_slice(),
|
||||||
|
encoded.deref(),
|
||||||
|
lmdb::put::Flags::empty(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
ImageFormat::WEBP
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
accessor.put(
|
||||||
|
&db_context.image_meta_store,
|
||||||
|
data_hash.as_slice(),
|
||||||
|
rkyv::to_bytes::<_, 256>(&ImageHeader { store_format })
|
||||||
|
.unwrap()
|
||||||
|
.as_slice(),
|
||||||
|
lmdb::put::Flags::empty(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
request.respond(Response::from_string(base64_url::encode(&data_hash)))?;
|
||||||
|
|
||||||
|
drop(accessor);
|
||||||
|
|
||||||
|
txn.commit()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_get<'u>(
|
||||||
|
mut request: Request,
|
||||||
|
db_context: &DatabaseContext,
|
||||||
|
params: matchit::Params<'u, 'u>,
|
||||||
|
) -> CrowResult<()> {
|
||||||
|
let (get_id, requested_format) = some_or_response!(
|
||||||
|
parse_base64_hash!(params.get("id")).zip(
|
||||||
|
params
|
||||||
|
.get("format")
|
||||||
|
.and_then(|s| ImageFormat::from_str(s).ok())
|
||||||
|
), or respond to request with Response::empty(400)
|
||||||
|
);
|
||||||
|
|
||||||
|
let content_type = requested_format.to_mime();
|
||||||
|
|
||||||
|
let read = db_context.read_txn()?;
|
||||||
|
let access = read.access();
|
||||||
|
|
||||||
|
let metadata: &ArchivedImageHeader = some_or_response!(
|
||||||
|
access
|
||||||
|
.get::<[u8], ArchivedImageHeader>(&db_context.image_meta_store, &get_id)
|
||||||
|
.to_opt()?,
|
||||||
|
or respond to request with
|
||||||
|
Response::empty(404)
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = access.get::<[u8], [u8]>(&db_context.image_store, &get_id)?;
|
||||||
|
|
||||||
|
if metadata.store_format == requested_format {
|
||||||
|
request.respond(Response::new(
|
||||||
|
200.into(),
|
||||||
|
vec![],
|
||||||
|
data,
|
||||||
|
Some(data.len()),
|
||||||
|
None,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let stored_image = image::load_from_memory_with_format(data, metadata.store_format.into())?;
|
||||||
|
|
||||||
|
use ColorType::*;
|
||||||
|
|
||||||
|
// catch unsupported transcoding (because of unsupported color format)
|
||||||
|
// and return a 500
|
||||||
|
match (stored_image.color(), requested_format) {
|
||||||
|
(Rgb8 | Rgba8, ImageFormat::WEBP) => (),
|
||||||
|
(Rgb8 | Rgba8 | L8 | La8, ImageFormat::JPG) => (),
|
||||||
|
(Rgb8 | Rgba8 | L8 | La8 | Rgba16 | Rgb16 | L16 | La16, ImageFormat::PNG) => (),
|
||||||
|
_ => {
|
||||||
|
request.respond(
|
||||||
|
response!(err 500 "unsupported color channels for the requested image format"),
|
||||||
|
)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut req_writer = request.extract_writer_impl();
|
||||||
|
|
||||||
|
let response = Response::new(
|
||||||
|
200.into(),
|
||||||
|
vec![Header::from_bytes(&b"Content-Type"[..], content_type.as_bytes()).unwrap()],
|
||||||
|
std::io::empty(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
Request::ignore_client_closing_errors(response.print_and_write(
|
||||||
|
&mut req_writer,
|
||||||
|
request.http_version().clone(),
|
||||||
|
request.headers(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|w, _| {
|
||||||
|
match requested_format {
|
||||||
|
ImageFormat::WEBP => {
|
||||||
|
// TODO: make compression quality here configurable
|
||||||
|
let mem = webp::Encoder::from_image(&stored_image)
|
||||||
|
.unwrap()
|
||||||
|
.encode(95.0);
|
||||||
|
w.write_all(mem.deref())
|
||||||
|
}
|
||||||
|
ImageFormat::JPG => JpegEncoder::new(w)
|
||||||
|
.encode_image(&stored_image)
|
||||||
|
.map_err(|e| std::io::Error::new(ErrorKind::Other, e)),
|
||||||
|
ImageFormat::PNG => PngEncoder::new(w)
|
||||||
|
.write_image(
|
||||||
|
stored_image.as_bytes(),
|
||||||
|
stored_image.width(),
|
||||||
|
stored_image.height(),
|
||||||
|
stored_image.color(),
|
||||||
|
)
|
||||||
|
.map_err(|e| std::io::Error::new(ErrorKind::Other, e)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Request::ignore_client_closing_errors(req_writer.flush())?;
|
||||||
|
if let Some(sender) = request.notify_when_responded.take() {
|
||||||
|
sender.send(()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(req_writer);
|
||||||
|
drop(request);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_to_handler!(image_upload: ImageUploadHandler);
|
||||||
|
fn_to_handler!(image_get: ImageGetHandler);
|
3
src/routes/mod.rs
Normal file
3
src/routes/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod get;
|
||||||
|
pub mod image;
|
||||||
|
pub mod upload;
|
68
src/routes/upload.rs
Normal file
68
src/routes/upload.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
use crate::*;
|
||||||
|
use blake2::{Blake2s256, Digest};
|
||||||
|
use lmdb::LmdbResultExt;
|
||||||
|
use lmdb_zero as lmdb;
|
||||||
|
use multipart::server::Multipart;
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
use tiny_http::{Request, Response};
|
||||||
|
|
||||||
|
fn upload<'u>(
|
||||||
|
mut request: Request,
|
||||||
|
db_context: &DatabaseContext,
|
||||||
|
_: matchit::Params<'u, 'u>,
|
||||||
|
) -> CrowResult<()> {
|
||||||
|
let mut entry = some_or_response!(
|
||||||
|
single_multipart!(&mut request), or respond to request with Response::empty(400)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut data: Vec<u8> = Vec::with_capacity(20000);
|
||||||
|
entry.data.read_to_end(&mut data)?;
|
||||||
|
|
||||||
|
let data_hash = Blake2s256::digest(&data);
|
||||||
|
|
||||||
|
let txn = db_context.write_txn()?;
|
||||||
|
|
||||||
|
let mut accessor = txn.access();
|
||||||
|
|
||||||
|
if accessor
|
||||||
|
.get::<[u8], [u8]>(&db_context.binary_store, data_hash.as_slice())
|
||||||
|
.to_opt()?
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
request.respond(Response::from_string(base64_url::encode(&data_hash)))?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
accessor.put(
|
||||||
|
&db_context.binary_store,
|
||||||
|
data_hash.as_slice(),
|
||||||
|
&data,
|
||||||
|
lmdb::put::Flags::empty(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let header = FileHeader {
|
||||||
|
file_name: entry.headers.filename,
|
||||||
|
content_type: entry
|
||||||
|
.headers
|
||||||
|
.content_type
|
||||||
|
.map(|v| v.essence_str().to_owned()),
|
||||||
|
};
|
||||||
|
|
||||||
|
accessor.put(
|
||||||
|
&db_context.metadata_store,
|
||||||
|
data_hash.as_slice(),
|
||||||
|
rkyv::to_bytes::<_, 256>(&header).unwrap().as_slice(),
|
||||||
|
lmdb::put::Flags::empty(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
request.respond(Response::from_string(base64_url::encode(&data_hash)))?;
|
||||||
|
|
||||||
|
drop(accessor);
|
||||||
|
|
||||||
|
txn.commit()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_to_handler!(upload: UploadHandler);
|
Loading…
Reference in a new issue