diff --git a/Cargo.lock b/Cargo.lock index 6c4d61f..91ab9a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,227 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93acb4a42f64936f9b8cae4a433b237599dd6eb6ed06124eb67132ef8cc90662" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "base64 0.22.1", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more 2.1.1", + "encoding_rs", + "flate2", + "foldhash 0.1.5", + "futures-core", + "h2 0.3.27", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand 0.10.1", + "sha1 0.11.0", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-multipart" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5118a26dee7e34e894f7e85aa0ee5080ae4c18bf03c0e30d49a80e418f00a53" +dependencies = [ + "actix-multipart-derive", + "actix-utils", + "actix-web", + "derive_more 0.99.20", + "futures-core", + "futures-util", + "httparse", + "local-waker", + "log", + "memchr", + "mime", + "rand 0.8.6", + "serde", + "serde_json", + "serde_plain", + "tempfile", + "tokio", +] + +[[package]] +name = "actix-multipart-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b" +dependencies = [ + "darling", + "parse-size", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more 2.1.1", + "encoding_rs", + "foldhash 0.1.5", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.6.4", + "time", + "tracing", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "adler2" version = "2.0.1" @@ -34,6 +255,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -61,6 +307,21 @@ dependencies = [ "equator", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -86,9 +347,11 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" name = "appks" version = "0.1.0" dependencies = [ + "actix-multipart", + "actix-web", "argon2", "async-nats", - "base64", + "base64 0.22.1", "captcha-rs", "chacha20poly1305", "chrono", @@ -200,7 +463,7 @@ version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad3cd6df81292728e2a8cb1f1dcb4d7e7a1ab59b80c14fbbcba2baf9d5cf86a" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-util", "memchr", @@ -359,7 +622,7 @@ dependencies = [ "axum-core 0.4.5", "bytes", "futures-util", - "http", + "http 1.4.1", "http-body", "http-body-util", "itoa", @@ -385,7 +648,7 @@ dependencies = [ "axum-core 0.5.6", "bytes", "futures-util", - "http", + "http 1.4.1", "http-body", "http-body-util", "itoa", @@ -410,7 +673,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 1.4.1", "http-body", "http-body-util", "mime", @@ -429,7 +692,7 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http", + "http 1.4.1", "http-body", "http-body-util", "mime", @@ -439,6 +702,12 @@ dependencies = [ "tower-service", ] +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "base64" version = "0.22.1" @@ -502,6 +771,27 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "brotli" +version = "8.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "built" version = "0.8.1" @@ -541,6 +831,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bytestring" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86566c496f2f47d9b8147a4c8b02ffdb69c919fe0c2b2e7195d22cbba0e635c9" +dependencies = [ + "bytes", +] + [[package]] name = "captcha-rs" version = "0.5.0" @@ -548,7 +847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea23e9ba29e482e553d48391849195b95f055ffb059f785a28c9c5a046844223" dependencies = [ "ab_glyph", - "base64", + "base64 0.22.1", "image", "imageproc", "rand 0.9.4", @@ -688,6 +987,45 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "aes-gcm", + "base64 0.20.0", + "hkdf 0.12.4", + "hmac 0.12.1", + "percent-encoding", + "rand 0.8.6", + "sha2 0.10.9", + "subtle", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -822,6 +1160,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "ctutils" version = "0.4.2" @@ -857,6 +1204,41 @@ dependencies = [ "syn", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "dashmap" version = "6.2.1" @@ -883,7 +1265,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "pem-rfc7468", "zeroize", ] @@ -898,6 +1280,42 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case 0.10.0", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -905,7 +1323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", + "const-oid 0.9.6", "crypto-common 0.1.6", "subtle", ] @@ -917,6 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", + "const-oid 0.10.2", "crypto-common 0.2.2", "ctutils", ] @@ -1026,7 +1445,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0452bcc559431b16f472b7ab86e2f9ccd5f3c2da3795afbd6b773665e047fe" dependencies = [ - "http", + "http 1.4.1", "prost 0.13.5", "tokio", "tokio-stream", @@ -1309,6 +1728,16 @@ dependencies = [ "wasip3", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gif" version = "0.14.2" @@ -1427,6 +1856,25 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0" +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.14" @@ -1438,7 +1886,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.4.1", "indexmap 2.14.0", "slab", "tokio", @@ -1552,6 +2000,17 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.4.1" @@ -1569,7 +2028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.1", ] [[package]] @@ -1580,7 +2039,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", + "http 1.4.1", "http-body", "pin-project-lite", ] @@ -1622,8 +2081,8 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", - "http", + "h2 0.4.14", + "http 1.4.1", "http-body", "httparse", "httpdate", @@ -1640,7 +2099,7 @@ version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http", + "http 1.4.1", "hyper", "hyper-util", "rustls", @@ -1669,11 +2128,11 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", - "http", + "http 1.4.1", "http-body", "hyper", "ipnet", @@ -1800,6 +2259,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.1.0" @@ -1880,6 +2345,12 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + [[package]] name = "indexmap" version = "1.9.3" @@ -2014,6 +2485,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.5.0" @@ -2079,6 +2556,23 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + [[package]] name = "lock_api" version = "0.4.14" @@ -2190,6 +2684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] @@ -2419,14 +2914,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", "chrono", "form_urlencoded", "futures-channel", "futures-core", "futures-util", - "http", + "http 1.4.1", "http-body-util", "humantime", "hyper", @@ -2506,6 +3001,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "parse-size" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b" + [[package]] name = "password-hash" version = "0.5.0" @@ -2642,6 +3143,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -3186,6 +3699,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + [[package]] name = "regex-syntax" version = "0.8.10" @@ -3198,12 +3717,12 @@ version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-core", "futures-util", - "h2", - "http", + "h2 0.4.14", + "http 1.4.1", "http-body", "http-body-util", "hyper", @@ -3240,12 +3759,12 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", - "h2", - "http", + "h2 0.4.14", + "http 1.4.1", "http-body", "http-body-util", "hyper", @@ -3300,7 +3819,7 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ - "const-oid", + "const-oid 0.9.6", "digest 0.10.7", "num-bigint-dig", "num-integer", @@ -3576,6 +4095,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + [[package]] name = "serde_repr" version = "0.1.20" @@ -3813,7 +4341,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "cfg-if", "chrono", @@ -3917,7 +4445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "chrono", @@ -3996,6 +4524,12 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -4234,11 +4768,11 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f591660438b3038dd04d16c938271c79e7e06260ad2ea2885a4861bfb238605d" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-core", "futures-sink", - "http", + "http 1.4.1", "httparse", "rand 0.8.6", "ring", @@ -4258,10 +4792,10 @@ dependencies = [ "async-stream", "async-trait", "axum 0.7.9", - "base64", + "base64 0.22.1", "bytes", - "h2", - "http", + "h2 0.4.14", + "http 1.4.1", "http-body", "http-body-util", "hyper", @@ -4287,10 +4821,10 @@ checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "axum 0.8.9", - "base64", + "base64 0.22.1", "bytes", - "h2", - "http", + "h2 0.4.14", + "http 1.4.1", "http-body", "http-body-util", "hyper", @@ -4409,7 +4943,7 @@ dependencies = [ "bitflags", "bytes", "futures-util", - "http", + "http 1.4.1", "http-body", "pin-project-lite", "tower 0.5.3", @@ -4559,6 +5093,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5335,6 +5875,34 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "zune-core" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 23bc8d8..7103e82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,8 @@ tokio-stream = "0.1" async-nats = "0.49" futures-util = "0.3" utoipa = { version = "5.5.0", features = ["uuid","chrono","auto_into_responses","actix_extras","decimal","macros"]} +actix-web = { version = "4", features = ["secure-cookies"] } +actix-multipart = "0.7" [build-dependencies] tonic-prost-build = "0.14.6" diff --git a/api/mod.rs b/api/mod.rs index e69de29..4a021d0 100644 --- a/api/mod.rs +++ b/api/mod.rs @@ -0,0 +1,3 @@ +pub mod openapi; +pub mod response; +pub mod routes; diff --git a/api/openapi.rs b/api/openapi.rs new file mode 100644 index 0000000..a801ffb --- /dev/null +++ b/api/openapi.rs @@ -0,0 +1,4 @@ +use utoipa::OpenApi; + +#[derive(OpenApi)] +pub struct OpenApiDoc; diff --git a/api/response.rs b/api/response.rs new file mode 100644 index 0000000..ccbf7dd --- /dev/null +++ b/api/response.rs @@ -0,0 +1,19 @@ +use serde::Serialize; + +#[derive(Debug, Serialize, utoipa::ToSchema)] +pub struct ApiResponse { + pub data: T, +} + +#[derive(Debug, Serialize, utoipa::ToSchema)] +pub struct ApiEmptyResponse { + pub message: String, +} + +#[derive(Debug, Serialize, utoipa::ToSchema)] +pub struct ApiListResponse { + pub data: Vec, + pub total: i64, + pub page: i64, + pub per_page: i64, +} diff --git a/api/routes.rs b/api/routes.rs new file mode 100644 index 0000000..0f28b67 --- /dev/null +++ b/api/routes.rs @@ -0,0 +1,6 @@ +use actix_web::web; +use actix_web::web::scope; + +pub fn init_routes(cfg: &mut web::ServiceConfig) { + cfg.service(scope("/api/v1")); +} diff --git a/error.rs b/error.rs index a5019ab..de1984f 100644 --- a/error.rs +++ b/error.rs @@ -1,3 +1,4 @@ +use actix_web::HttpResponse; use thiserror::Error; pub type AppResult = Result; @@ -103,3 +104,45 @@ pub enum AppError { #[error("transaction error")] TxnError, } + +impl actix_web::ResponseError for AppError { + fn status_code(&self) -> actix_web::http::StatusCode { + use actix_web::http::StatusCode; + match self { + AppError::Unauthorized => StatusCode::UNAUTHORIZED, + AppError::Forbidden(_) => StatusCode::FORBIDDEN, + AppError::NotFound(_) | AppError::UserNotFound => StatusCode::NOT_FOUND, + AppError::BadRequest(_) | AppError::Parse(_) | AppError::CaptchaError => { + StatusCode::BAD_REQUEST + } + AppError::Conflict(_) | AppError::AccountAlreadyExists | AppError::EmailExists => { + StatusCode::CONFLICT + } + AppError::QuotaExceeded(_) => StatusCode::TOO_MANY_REQUESTS, + AppError::InvalidPassword + | AppError::PasswordTooWeak + | AppError::InvalidTwoFactorCode + | AppError::TwoFactorRequired + | AppError::TwoFactorAlreadyEnabled + | AppError::TwoFactorNotSetup + | AppError::TwoFactorNotEnabled + | AppError::InvalidResetToken + | AppError::ResetTokenExpired + | AppError::InvalidEmailCode + | AppError::RsaDecodeError + | AppError::RsaGenerationError => StatusCode::BAD_REQUEST, + AppError::PasswordHashError(_) => StatusCode::INTERNAL_SERVER_ERROR, + _ => StatusCode::INTERNAL_SERVER_ERROR, + } + } + + fn error_response(&self) -> HttpResponse { + let status = self.status_code(); + let message = if status == actix_web::http::StatusCode::INTERNAL_SERVER_ERROR { + "internal server error".to_string() + } else { + self.to_string() + }; + HttpResponse::build(status).json(serde_json::json!({ "error": message })) + } +} diff --git a/lib.rs b/lib.rs index ba748ff..e622600 100644 --- a/lib.rs +++ b/lib.rs @@ -1,3 +1,4 @@ +pub mod api; pub mod cache; pub mod config; pub mod error; @@ -9,4 +10,3 @@ pub mod queue; pub mod service; pub mod session; pub mod storage; -pub mod api; \ No newline at end of file diff --git a/service/workspace/core.rs b/service/workspace/core.rs index 307c5af..1b45f8c 100644 --- a/service/workspace/core.rs +++ b/service/workspace/core.rs @@ -541,6 +541,19 @@ impl WorkspaceService { .ok_or(AppError::NotFound("workspace not found".into())) } + pub(crate) async fn find_workspace_by_name(&self, name: &str) -> Result { + sqlx::query_as::<_, Workspace>( + "SELECT id, owner_id, name, description, avatar_url, visibility, plan, status, \ + default_role, is_personal, archived_at, created_at, updated_at, deleted_at \ + FROM workspace WHERE lower(name) = lower($1) AND deleted_at IS NULL", + ) + .bind(name) + .fetch_optional(self.ctx.db.reader()) + .await + .map_err(AppError::Database)? + .ok_or(AppError::NotFound("workspace not found".into())) + } + pub async fn workspace_user_role( &self, user_uid: Uuid, diff --git a/session/mod.rs b/session/mod.rs index f85edc1..d4705b1 100644 --- a/session/mod.rs +++ b/session/mod.rs @@ -1,9 +1,11 @@ pub mod config; +pub mod middleware; #[allow(clippy::module_inception)] pub mod session; pub mod storage; pub use self::{ + middleware::{CookieContentSecurity, SessionMiddleware, TtlExtensionPolicy}, session::{Session, SessionState, SessionStatus, SessionUser}, storage::{RedisSessionStore, SessionKey, SessionStore, generate_session_key}, }; diff --git a/session/session.rs b/session/session.rs index 0c2042a..44c3a29 100644 --- a/session/session.rs +++ b/session/session.rs @@ -1,6 +1,10 @@ use std::cell::{Ref, RefCell}; +use std::future::{Ready, ready}; +use std::mem; use std::rc::Rc; +use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse}; +use actix_web::{FromRequest, HttpMessage, HttpRequest}; use serde::Serialize; use serde::de::DeserializeOwned; use serde_json::{Map, Value}; @@ -10,21 +14,30 @@ use crate::error::AppError; const SESSION_USER_KEY: &str = "session:user_uid"; +/// Request-local session handle. +/// +/// Clones share the same interior state through request extensions, so changes +/// made by extractors/handlers can be collected by `SessionMiddleware` at the +/// end of the request lifecycle. #[derive(Clone)] pub struct Session(Rc>); #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum SessionStatus { + /// Session state has changed and must be persisted. Changed, + /// Session and cookie must be deleted. Further mutations are ignored. Purged, + /// Session key must be regenerated while preserving current state. Renewed, + /// Session has not been modified during this request. #[default] Unchanged, } #[derive(Default)] struct SessionInner { - state: Map, + state: SessionState, status: SessionStatus, } @@ -33,7 +46,7 @@ impl Session { Self::default() } - pub fn from_state(state: Map) -> Self { + pub fn from_state(state: SessionState) -> Self { Self(Rc::new(RefCell::new(SessionInner { state, status: SessionStatus::Unchanged, @@ -41,34 +54,36 @@ impl Session { } pub fn get(&self, key: &str) -> Result, AppError> { - if let Some(value) = self.0.borrow().state.get(key) { - Ok(Some(serde_json::from_value(value.clone())?)) - } else { - Ok(None) - } + let value = self.0.borrow().state.get(key).cloned(); + value + .map(serde_json::from_value) + .transpose() + .map_err(AppError::Json) } pub fn contains_key(&self, key: &str) -> bool { self.0.borrow().state.contains_key(key) } - pub fn entries(&self) -> Ref<'_, Map> { + pub fn entries(&self) -> Ref<'_, SessionState> { Ref::map(self.0.borrow(), |inner| &inner.state) } pub fn status(&self) -> SessionStatus { - Ref::map(self.0.borrow(), |inner| &inner.status).clone() + self.0.borrow().status.clone() + } + + pub fn is_empty(&self) -> bool { + self.0.borrow().state.is_empty() } pub fn insert(&self, key: impl Into, value: T) -> Result<(), AppError> { + let value = serde_json::to_value(value)?; let mut inner = self.0.borrow_mut(); if inner.status != SessionStatus::Purged { - if inner.status != SessionStatus::Renewed { - inner.status = SessionStatus::Changed; - } - let val = serde_json::to_value(&value)?; - inner.state.insert(key.into(), val); + mark_changed(&mut inner.status); + inner.state.insert(key.into(), value); } Ok(()) @@ -83,22 +98,20 @@ impl Session { F: FnOnce(T) -> T, { let mut inner = self.0.borrow_mut(); - let key_str = key.into(); + let key = key.into(); - if let Some(val) = inner.state.get(&key_str) { - if inner.status == SessionStatus::Purged { - return Ok(()); - } - - let value: T = serde_json::from_value(val.clone())?; - let updated = serde_json::to_value(updater(value))?; - - if inner.status != SessionStatus::Renewed { - inner.status = SessionStatus::Changed; - } - inner.state.insert(key_str, updated); + if inner.status == SessionStatus::Purged { + return Ok(()); } + let Some(value) = inner.state.get(&key).cloned() else { + return Ok(()); + }; + + let value = serde_json::from_value(value)?; + let updated = serde_json::to_value(updater(value))?; + mark_changed(&mut inner.status); + inner.state.insert(key, updated); Ok(()) } @@ -121,14 +134,12 @@ impl Session { pub fn remove(&self, key: &str) -> Option { let mut inner = self.0.borrow_mut(); - if inner.status != SessionStatus::Purged { - if inner.status != SessionStatus::Renewed { - inner.status = SessionStatus::Changed; - } - return inner.state.remove(key); + if inner.status == SessionStatus::Purged { + return None; } - None + mark_changed(&mut inner.status); + inner.state.remove(key) } pub fn remove_as(&self, key: &str) -> Option> { @@ -140,9 +151,7 @@ impl Session { let mut inner = self.0.borrow_mut(); if inner.status != SessionStatus::Purged { - if inner.status != SessionStatus::Renewed { - inner.status = SessionStatus::Changed; - } + mark_changed(&mut inner.status); inner.state.clear(); } } @@ -174,13 +183,45 @@ impl Session { } pub fn take_state(&self) -> SessionState { - let mut inner = self.0.borrow_mut(); - std::mem::take(&mut inner.state) + mem::take(&mut self.0.borrow_mut().state) } pub fn mark_unchanged(&self) { self.0.borrow_mut().status = SessionStatus::Unchanged; } + + pub(crate) fn set_session(req: &mut ServiceRequest, state: SessionState) { + let session = Self::get_session(&mut req.extensions_mut()); + let mut inner = session.0.borrow_mut(); + inner.state = state; + inner.status = SessionStatus::Unchanged; + } + + pub(crate) fn get_changes(res: &mut ServiceResponse) -> (SessionStatus, SessionState) { + let Some(inner) = res + .request() + .extensions() + .get::>>() + .cloned() + else { + return (SessionStatus::Unchanged, SessionState::new()); + }; + + let mut inner = inner.borrow_mut(); + let status = inner.status.clone(); + let state = mem::take(&mut inner.state); + (status, state) + } + + pub(crate) fn get_session(extensions: &mut Extensions) -> Session { + if let Some(inner) = extensions.get::>>() { + return Session(Rc::clone(inner)); + } + + let inner = Rc::new(RefCell::new(SessionInner::default())); + extensions.insert(Rc::clone(&inner)); + Session(inner) + } } impl Default for Session { @@ -193,3 +234,18 @@ pub type SessionState = Map; #[derive(Debug, Clone, Copy)] pub struct SessionUser(pub Uuid); + +impl FromRequest for Session { + type Error = AppError; + type Future = Ready>; + + fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { + ready(Ok(Self::get_session(&mut req.extensions_mut()))) + } +} + +fn mark_changed(status: &mut SessionStatus) { + if *status != SessionStatus::Renewed { + *status = SessionStatus::Changed; + } +}