mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 05:20:58 -05:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
358c1efc45 | ||
|
|
1dbe3d8b1e | ||
|
|
8da68b009c | ||
|
|
e4bc031fbb | ||
|
|
d937a7f78f | ||
|
|
fef96c3835 | ||
|
|
b9e793098c | ||
|
|
222f3e958d | ||
|
|
4ab06030dc |
Generated
+127
-157
@@ -2,31 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "actix"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b"
|
||||
dependencies = [
|
||||
"actix-macros",
|
||||
"actix-rt",
|
||||
"actix_derive",
|
||||
"bitflags 2.11.0",
|
||||
"bytes",
|
||||
"crossbeam-channel",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.5",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-codec"
|
||||
version = "0.5.2"
|
||||
@@ -46,13 +21,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-cors"
|
||||
version = "0.6.4"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e"
|
||||
checksum = "daa239b93927be1ff123eebada5a3ff23e89f0124ccb8609234e5103d5a5ae6d"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"derive_more 0.99.17",
|
||||
"derive_more 2.0.1",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -61,25 +36,25 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.7.0"
|
||||
version = "3.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb9843d84c775696c37d9a418bbb01b932629d01870722c0f13eb3f95e2536d"
|
||||
checksum = "93acb4a42f64936f9b8cae4a433b237599dd6eb6ed06124eb67132ef8cc90662"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"ahash 0.8.12",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.11.0",
|
||||
"brotli 6.0.0",
|
||||
"brotli",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"derive_more 0.99.17",
|
||||
"derive_more 2.0.1",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"foldhash 0.1.5",
|
||||
"futures-core",
|
||||
"h2 0.3.26",
|
||||
"h2 0.3.27",
|
||||
"http 0.2.11",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
@@ -89,8 +64,8 @@ dependencies = [
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rand 0.8.5",
|
||||
"sha1 0.10.6",
|
||||
"rand 0.10.1",
|
||||
"sha1 0.11.0",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -110,14 +85,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-multipart"
|
||||
version = "0.6.2"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d974dd6c4f78d102d057c672dcf6faa618fafa9df91d44f9c466688fc1275a3a"
|
||||
checksum = "d5118a26dee7e34e894f7e85aa0ee5080ae4c18bf03c0e30d49a80e418f00a53"
|
||||
dependencies = [
|
||||
"actix-multipart-derive",
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"bytes",
|
||||
"derive_more 0.99.17",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
@@ -136,9 +110,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-multipart-derive"
|
||||
version = "0.6.1"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a0a77f836d869f700e5b47ac7c3c8b9c8bc82e4aec861954c6198abee3ebd4d"
|
||||
checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b"
|
||||
dependencies = [
|
||||
"darling 0.20.10",
|
||||
"parse-size",
|
||||
@@ -149,9 +123,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-router"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
|
||||
checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
|
||||
dependencies = [
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
@@ -164,9 +138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-rt"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208"
|
||||
checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63"
|
||||
dependencies = [
|
||||
"actix-macros",
|
||||
"futures-core",
|
||||
@@ -175,16 +149,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-server"
|
||||
version = "2.4.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b02303ce8d4e8be5b855af6cf3c3a08f3eff26880faad82bab679c22d3650cb5"
|
||||
checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502"
|
||||
dependencies = [
|
||||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"mio 0.8.11",
|
||||
"mio",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -212,9 +186,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-web"
|
||||
version = "4.7.0"
|
||||
version = "4.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d6316df3fa569627c98b12557a8b6ff0674e5be4bb9b5e4ae2550ddb4964ed6"
|
||||
checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-http",
|
||||
@@ -225,15 +199,16 @@ dependencies = [
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"actix-web-codegen",
|
||||
"ahash 0.8.12",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"cookie 0.16.2",
|
||||
"derive_more 0.99.17",
|
||||
"derive_more 2.0.1",
|
||||
"encoding_rs",
|
||||
"foldhash 0.1.5",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"impl-more 0.1.6",
|
||||
"itoa 1.0.11",
|
||||
"language-tags",
|
||||
"log",
|
||||
@@ -246,29 +221,12 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2 0.5.10",
|
||||
"socket2 0.6.2",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web-actors"
|
||||
version = "4.3.1+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98c5300b38fd004fe7d2a964f9a90813fdbe8a81fed500587e78b1b71c6f980"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-codec",
|
||||
"actix-http",
|
||||
"actix-web",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web-codegen"
|
||||
version = "4.3.0"
|
||||
@@ -283,9 +241,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-web-lab"
|
||||
version = "0.20.2"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7675c1a84eec1b179c844cdea8488e3e409d8e4984026e92fa96c87dd86f33c6"
|
||||
checksum = "1a89606b86fd2f8c0ddd72ef0be171e58c919ee0b6914aa7b52354063e5ee361"
|
||||
dependencies = [
|
||||
"actix-http",
|
||||
"actix-router",
|
||||
@@ -295,37 +253,36 @@ dependencies = [
|
||||
"actix-web-lab-derive",
|
||||
"ahash 0.8.12",
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"csv",
|
||||
"derive_more 0.99.17",
|
||||
"derive_more 2.0.1",
|
||||
"form_urlencoded",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 0.2.11",
|
||||
"impl-more",
|
||||
"itertools 0.12.1",
|
||||
"impl-more 0.3.1",
|
||||
"itertools 0.14.0",
|
||||
"local-channel",
|
||||
"mediatype",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_html_form",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web-lab-derive"
|
||||
version = "0.20.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aa0b287c8de4a76b691f29dbb5451e8dd5b79d777eaf87350c9b0cbfdb5e968"
|
||||
checksum = "2ac4d6e04e97fe707286509b4f338e99c5fb7249c770e1da074af5e27faa96b3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
@@ -363,17 +320,6 @@ dependencies = [
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix_derive"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix_helpers"
|
||||
version = "0.0.1"
|
||||
@@ -405,7 +351,7 @@ checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher 0.3.0",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.12",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
@@ -417,7 +363,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher 0.4.4",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -893,7 +839,7 @@ version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8"
|
||||
dependencies = [
|
||||
"brotli 8.0.2",
|
||||
"brotli",
|
||||
"flate2",
|
||||
"futures-core",
|
||||
"memchr",
|
||||
@@ -1777,17 +1723,6 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor 4.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "8.0.2"
|
||||
@@ -1796,17 +1731,7 @@ checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor 5.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2155,6 +2080,17 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
@@ -2733,6 +2669,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.0.1"
|
||||
@@ -4686,6 +4631,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"rand_core 0.10.1",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
"wasm-bindgen",
|
||||
@@ -5062,9 +5008,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.26"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -5503,7 +5449,7 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.26",
|
||||
"h2 0.3.27",
|
||||
"http 0.2.11",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
@@ -5888,6 +5834,12 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d"
|
||||
|
||||
[[package]]
|
||||
name = "impl-more"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -6102,6 +6054,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
@@ -6349,7 +6310,7 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6983,12 +6944,6 @@ dependencies = [
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mediatype"
|
||||
version = "0.19.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf0bc9784973713e4a90d515a4302991ca125a7c4516951cb607f2298cb757e5"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
@@ -7189,18 +7144,6 @@ dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.2"
|
||||
@@ -7209,6 +7152,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@@ -9268,6 +9212,17 @@ dependencies = [
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
|
||||
dependencies = [
|
||||
"chacha20",
|
||||
"getrandom 0.4.1",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
@@ -9340,6 +9295,12 @@ dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
@@ -9661,7 +9622,7 @@ dependencies = [
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.26",
|
||||
"h2 0.3.27",
|
||||
"http 0.2.11",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.28",
|
||||
@@ -10685,15 +10646,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_html_form"
|
||||
version = "0.2.3"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224e6a14f315852940f3ec103125aa6482f0e224732ed91ed3330ed633077c34"
|
||||
checksum = "0946d52b4b7e28823148aebbeceb901012c595ad737920d504fa8634bb099e6f"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"indexmap 2.13.0",
|
||||
"itoa 1.0.11",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10957,10 +10918,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.12",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"digest 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
@@ -10975,7 +10947,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.12",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
@@ -10987,7 +10959,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.12",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
@@ -10998,7 +10970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.12",
|
||||
"digest 0.11.2",
|
||||
]
|
||||
|
||||
@@ -11519,12 +11491,10 @@ checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
|
||||
name = "storyteller-web"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-http",
|
||||
"actix-multipart",
|
||||
"actix-service",
|
||||
"actix-web",
|
||||
"actix-web-actors",
|
||||
"actix-web-lab",
|
||||
"actix_artcraft",
|
||||
"actix_cors_configs",
|
||||
@@ -12370,7 +12340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli 8.0.2",
|
||||
"brotli",
|
||||
"ico",
|
||||
"json-patch",
|
||||
"plist",
|
||||
@@ -12606,7 +12576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"brotli 8.0.2",
|
||||
"brotli",
|
||||
"cargo_metadata",
|
||||
"ctor",
|
||||
"dunce",
|
||||
@@ -12894,7 +12864,7 @@ checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio 1.0.2",
|
||||
"mio",
|
||||
"parking_lot 0.12.5",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
@@ -12968,9 +12938,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.16"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
|
||||
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@@ -13164,7 +13134,7 @@ dependencies = [
|
||||
"axum",
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"h2 0.3.26",
|
||||
"h2 0.3.27",
|
||||
"http 0.2.11",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.28",
|
||||
@@ -15269,7 +15239,7 @@ checksum = "f79937f6c4df65b3f6f78715b9de2977afe9ee3b3436483c7949a24511e25935"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"boring2",
|
||||
"brotli 8.0.2",
|
||||
"brotli",
|
||||
"bytes",
|
||||
"cookie 0.18.1",
|
||||
"flate2",
|
||||
|
||||
+6
-8
@@ -295,16 +295,14 @@ worldlabs_consumer_client = { path = "crates/api_clients/worldlabs_consumer_clie
|
||||
# External libs
|
||||
# Centrally pinned versions so that we can uniformly upgrade/downgrade versions across the workspace
|
||||
# If a crate needs to use an older/newer version, the dependency should be managed in that crate's own Cargo.toml
|
||||
actix = "0.13.5"
|
||||
actix-cors = "0.6.4" # TODO(bt): Carefully upgrade this. CORS broke last time.
|
||||
actix-http = "3.7.0"
|
||||
actix-multipart = "0.6.2"
|
||||
actix-rt = "2.10.0"
|
||||
actix-cors = "0.7.1"
|
||||
actix-http = "3.12.1"
|
||||
actix-multipart = "0.7.2"
|
||||
actix-rt = "2.11.0"
|
||||
actix-service = "2.0.3"
|
||||
actix-utils = "3.0.1"
|
||||
actix-web = "4.7.0" # TODO(bt): We're behind and need to bump versions.
|
||||
actix-web-actors = "4.3.1+deprecated"
|
||||
actix-web-lab = "0.20.2"
|
||||
actix-web = "4.13.0"
|
||||
actix-web-lab = "0.26.0"
|
||||
anyhow = "1.0.102"
|
||||
assertor = "0.0.3"
|
||||
base64 = "0.22.1"
|
||||
|
||||
@@ -65,7 +65,13 @@ fn do_build_cors_config(is_production: bool) -> Cors {
|
||||
}
|
||||
|
||||
// Remaining setup
|
||||
cors.allowed_methods(vec!["GET", "POST", "OPTIONS", "DELETE"])
|
||||
//
|
||||
// NB: actix-cors 0.7 flipped the `block_on_origin_mismatch` default to
|
||||
// false (mismatched origins get a 200 without CORS headers and the browser
|
||||
// enforces the block). We keep the long-standing 0.6 behavior of rejecting
|
||||
// mismatched origins server-side with a 400.
|
||||
cors.block_on_origin_mismatch(true)
|
||||
.allowed_methods(vec!["GET", "POST", "PUT", "OPTIONS", "DELETE"])
|
||||
.supports_credentials()
|
||||
.allowed_headers(vec![
|
||||
actix_http::header::ACCEPT,
|
||||
@@ -82,11 +88,70 @@ fn do_build_cors_config(is_production: bool) -> Cors {
|
||||
mod tests {
|
||||
use server_environment::ServerEnvironment;
|
||||
|
||||
use crate::testing::assert_no_origin_header_ok;
|
||||
use crate::testing::assert_origin_invalid;
|
||||
use crate::testing::assert_origin_ok;
|
||||
use crate::testing::assert_preflight_method_invalid;
|
||||
use crate::testing::assert_preflight_method_ok;
|
||||
|
||||
use super::build_cors_config;
|
||||
|
||||
/// Every production origin that hosts a product (or IS a backend that
|
||||
/// serves same-origin pages like swagger) must be allowed. This is the
|
||||
/// guard against `block_on_origin_mismatch(true)` locking out a real
|
||||
/// frontend after a CORS-crate upgrade.
|
||||
#[actix_rt::test]
|
||||
async fn test_production_product_and_backend_origins_are_allowed() {
|
||||
let production_cors = build_cors_config(ServerEnvironment::Production);
|
||||
|
||||
let origins = [
|
||||
// Backends (same-origin browser requests still carry an Origin header).
|
||||
"https://api.fakeyou.com",
|
||||
"https://api.storyteller.ai",
|
||||
// Products
|
||||
"https://fakeyou.com",
|
||||
"https://storyteller.ai",
|
||||
"https://app.getartcraft.com",
|
||||
"https://getartcraft.com",
|
||||
"https://www.getartcraft.com",
|
||||
"https://artcraft.ai",
|
||||
"https://www.artcraft.ai",
|
||||
"https://artcraft-dashboard.netlify.app",
|
||||
];
|
||||
for origin in origins {
|
||||
assert_origin_ok(&production_cors, origin).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-browser clients (curl, python API clients, server-to-server) send
|
||||
/// no Origin header and must never be blocked, even with
|
||||
/// `block_on_origin_mismatch(true)`.
|
||||
#[actix_rt::test]
|
||||
async fn test_requests_without_origin_header_are_not_blocked() {
|
||||
let production_cors = build_cors_config(ServerEnvironment::Production);
|
||||
assert_no_origin_header_ok(&production_cors).await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_preflight_allows_every_supported_method() {
|
||||
let production_cors = build_cors_config(ServerEnvironment::Production);
|
||||
|
||||
// PUT is used by e.g. the folders routes (rename/star/color_code/cover_image).
|
||||
for method in ["GET", "POST", "PUT", "DELETE"] {
|
||||
assert_preflight_method_ok(&production_cors, "https://app.getartcraft.com", method).await;
|
||||
assert_preflight_method_ok(&production_cors, "https://storyteller.ai", method).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_preflight_rejects_unsupported_methods() {
|
||||
let production_cors = build_cors_config(ServerEnvironment::Production);
|
||||
|
||||
for method in ["PATCH", "TRACE", "CONNECT"] {
|
||||
assert_preflight_method_invalid(&production_cors, "https://app.getartcraft.com", method).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_fakeyou_production() {
|
||||
let production_cors = build_cors_config(ServerEnvironment::Production);
|
||||
|
||||
@@ -2,6 +2,7 @@ use actix_cors::Cors;
|
||||
use actix_http::body::{BoxBody, EitherBody};
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::dev::{ServiceResponse, Transform};
|
||||
use actix_web::http::Method;
|
||||
use actix_web::test;
|
||||
use actix_web::test::TestRequest;
|
||||
use speculoos::asserting;
|
||||
@@ -20,6 +21,39 @@ pub (crate) async fn assert_origin_invalid(cors: &Cors, hostname: &str) {
|
||||
.is_equal_to(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
/// Assert that a CORS preflight (OPTIONS + Access-Control-Request-Method)
|
||||
/// for the given method is accepted from the given origin.
|
||||
pub (crate) async fn assert_preflight_method_ok(cors: &Cors, hostname: &str, method: &str) {
|
||||
let response = make_preflight_request(cors, hostname, method).await;
|
||||
asserting(&format!("Preflight for {} from {} is allowed", method, hostname))
|
||||
.that(&response.status())
|
||||
.is_equal_to(StatusCode::OK);
|
||||
}
|
||||
|
||||
/// Assert that a CORS preflight for the given method is rejected.
|
||||
pub (crate) async fn assert_preflight_method_invalid(cors: &Cors, hostname: &str, method: &str) {
|
||||
let response = make_preflight_request(cors, hostname, method).await;
|
||||
asserting(&format!("Preflight for {} from {} is rejected", method, hostname))
|
||||
.that(&response.status())
|
||||
.is_equal_to(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
/// Assert that a request WITHOUT an Origin header (curl, server-to-server,
|
||||
/// native API clients) is never blocked by CORS, regardless of
|
||||
/// `block_on_origin_mismatch`.
|
||||
pub (crate) async fn assert_no_origin_header_ok(cors: &Cors) {
|
||||
let cors = cors.new_transform(test::ok_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let request = TestRequest::default().to_srv_request();
|
||||
let response = test::call_service(&cors, request).await;
|
||||
|
||||
asserting("Requests without an Origin header are not blocked")
|
||||
.that(&response.status())
|
||||
.is_equal_to(StatusCode::OK);
|
||||
}
|
||||
|
||||
async fn make_test_request(cors: &Cors, hostname: &str) -> ServiceResponse<EitherBody<BoxBody>> {
|
||||
let cors= cors.new_transform(test::ok_service())
|
||||
.await
|
||||
@@ -31,3 +65,21 @@ async fn make_test_request(cors: &Cors, hostname: &str) -> ServiceResponse<Eithe
|
||||
|
||||
test::call_service(&cors, request).await
|
||||
}
|
||||
|
||||
async fn make_preflight_request(
|
||||
cors: &Cors,
|
||||
hostname: &str,
|
||||
method: &str,
|
||||
) -> ServiceResponse<EitherBody<BoxBody>> {
|
||||
let cors = cors.new_transform(test::ok_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let request = TestRequest::default()
|
||||
.method(Method::OPTIONS)
|
||||
.insert_header(("Origin", hostname))
|
||||
.insert_header(("Access-Control-Request-Method", method))
|
||||
.to_srv_request();
|
||||
|
||||
test::call_service(&cors, request).await
|
||||
}
|
||||
|
||||
+2
-3
@@ -84,9 +84,8 @@ pub struct SessionUserRecord {
|
||||
}
|
||||
|
||||
impl SessionUserRecord {
|
||||
// TODO(bt, 2022-12-20): Convert all users of the bare record to using `UserToken`, then get rid of this method.
|
||||
pub fn get_strongly_typed_user_token(&self) -> UserToken {
|
||||
self.user_token.clone()
|
||||
pub fn get_user_token(&self) -> &UserToken {
|
||||
&self.user_token
|
||||
}
|
||||
|
||||
pub fn is_mod(&self) -> bool {
|
||||
|
||||
+7
-6
@@ -1,8 +1,7 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use errors::AnyhowResult;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::MySql;
|
||||
use sqlx::{Executor, MySql};
|
||||
|
||||
pub struct ActiveUserSubscription {
|
||||
pub user_token: String,
|
||||
@@ -19,10 +18,12 @@ pub struct ActiveUserSubscription {
|
||||
pub subscription_expires_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub async fn list_active_user_subscriptions(
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
pub async fn list_active_user_subscriptions<'e, 'c : 'e, E>(
|
||||
mysql_executor: E,
|
||||
user_token: &str
|
||||
) -> AnyhowResult<Vec<ActiveUserSubscription>> {
|
||||
) -> AnyhowResult<Vec<ActiveUserSubscription>>
|
||||
where E: 'e + Executor<'c, Database = MySql>
|
||||
{
|
||||
// NB: "status=incomplete" subscriptions can happen when a user submits credit card info,
|
||||
// but the CRC is wrong. Eg. what happened with ftx on 2022.11.18.
|
||||
//
|
||||
@@ -45,7 +46,7 @@ WHERE
|
||||
"#,
|
||||
user_token,
|
||||
)
|
||||
.fetch_all(&mut **mysql_connection)
|
||||
.fetch_all(mysql_executor)
|
||||
.await?;
|
||||
|
||||
let records = records.into_iter()
|
||||
|
||||
@@ -162,12 +162,10 @@ tempfile.workspace = true
|
||||
# https://github.com/durch/rust-s3/pull/254
|
||||
rust-s3.workspace = true
|
||||
|
||||
actix.workspace = true
|
||||
actix-http.workspace = true
|
||||
actix-multipart.workspace = true
|
||||
actix-service.workspace = true
|
||||
actix-web.workspace = true
|
||||
actix-web-actors.workspace = true
|
||||
actix-web-lab.workspace = true
|
||||
|
||||
# Crypto
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use log::info;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use shared_env_var_config::mysql::env_get_mysql_connection_string_or_default;
|
||||
use sqlx::mysql::MySqlPoolOptions;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
pub async fn connect_to_database() -> AnyhowResult<MySqlPool>{
|
||||
pub async fn connect_to_database() -> AnyhowResult<MySqlPool> {
|
||||
let db_connection_string = env_get_mysql_connection_string_or_default();
|
||||
|
||||
let pool_options = MySqlPoolOptions::new()
|
||||
.max_connections(easyenv::get_env_num("MYSQL_MAX_CONNECTIONS", 5)?);
|
||||
// NB: The managed DB's connection ceiling is far above what we use, so the binding
|
||||
// constraint is this per-pod pool. Keep `replicas * MYSQL_MAX_CONNECTIONS` (plus the
|
||||
// background jobs' small pools) comfortably under the server's `max_connections`.
|
||||
let max_connections = easyenv::get_env_num("MYSQL_MAX_CONNECTIONS", 20)?;
|
||||
let min_connections = easyenv::get_env_num("MYSQL_MIN_CONNECTIONS", 0)?;
|
||||
|
||||
// TODO(bt,2024-09-17): Configure logging, etc. after we've upgraded sqlx
|
||||
// NB: Fail fast under pool saturation rather than stacking up on sqlx's 30s default — a
|
||||
// shorter timeout sheds load and surfaces the problem instead of hiding it behind long waits.
|
||||
let acquire_timeout_seconds = easyenv::get_env_num::<u64>("MYSQL_ACQUIRE_TIMEOUT_SECONDS", 10)?;
|
||||
|
||||
// NB: Log the settings before building/connecting so a connect failure has context. We do NOT
|
||||
// log the connection string (it contains credentials).
|
||||
info!(
|
||||
"Building MySQL pool: max_connections={}, min_connections={}, acquire_timeout_seconds={}",
|
||||
max_connections, min_connections, acquire_timeout_seconds,
|
||||
);
|
||||
|
||||
let pool_options = MySqlPoolOptions::new()
|
||||
.max_connections(max_connections)
|
||||
.min_connections(min_connections)
|
||||
.acquire_timeout(Duration::from_secs(acquire_timeout_seconds));
|
||||
|
||||
let pool = pool_options
|
||||
.connect(&db_connection_string)
|
||||
|
||||
+1
-27
@@ -2,7 +2,6 @@ use std::fmt::{Display, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::session::session_checker_error::SessionCheckerError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::RequireUserSessionError;
|
||||
use actix_artcraft::sessions::anonymous_visitor_tracking::avt_cookie_payload_error::AvtCookiePayloadError;
|
||||
use actix_artcraft::sessions::user_sessions::http_user_session_payload_error::HttpUserSessionPayloadError;
|
||||
use actix_http::StatusCode;
|
||||
@@ -19,7 +18,7 @@ use serde::Serialize;
|
||||
/// ```ignore
|
||||
/// pub async fn my_handler(...) -> Result<Json<MyResponse>, CommonWebError> {
|
||||
/// // sqlx errors, anyhow errors, session errors — all convert via ?
|
||||
/// let user = require_user_session_using_connection(&req, ...)?;
|
||||
/// let user = require_user_session(&req, ...)?;
|
||||
/// let data = some_db_query(&pool).await?;
|
||||
///
|
||||
/// if data.is_none() {
|
||||
@@ -317,15 +316,6 @@ impl From<serde_json::Error> for CommonWebError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequireUserSessionError> for CommonWebError {
|
||||
fn from(value: RequireUserSessionError) -> Self {
|
||||
match value {
|
||||
RequireUserSessionError::NotAuthorized => Self::NotAuthorized,
|
||||
RequireUserSessionError::ServerError => Self::from_error(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HttpUserSessionPayloadError> for CommonWebError {
|
||||
fn from(err: HttpUserSessionPayloadError) -> Self {
|
||||
if err.is_server_error() {
|
||||
@@ -545,22 +535,6 @@ mod tests {
|
||||
assert!(source.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn require_user_session_not_authorized_maps_to_401() {
|
||||
let error: CommonWebError = RequireUserSessionError::NotAuthorized.into();
|
||||
assert_eq!(error.status_code(), StatusCode::UNAUTHORIZED);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn require_user_session_server_error_wraps_cause() {
|
||||
let error: CommonWebError = RequireUserSessionError::ServerError.into();
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
assert!(error.cause().is_some());
|
||||
assert!(format!("{}", error.cause().unwrap()).contains("ServerError"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_json_error_converts_to_500() {
|
||||
let bad_json = "not json at all{{{";
|
||||
|
||||
+3
-2
@@ -120,7 +120,8 @@ pub async fn create_scene_handler(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.as_ref()
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
// ==================== FILE DATA ==================== //
|
||||
|
||||
@@ -166,7 +167,7 @@ pub async fn create_scene_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Dimensional),
|
||||
media_file_type: MediaFileType::SceneRon,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: Visibility::Public,
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
|
||||
let mut field_name = None;
|
||||
let mut field_filename = None;
|
||||
|
||||
if let content_disposition = field.content_disposition() {
|
||||
if let Some(content_disposition) = field.content_disposition() {
|
||||
field_name = content_disposition.get_name()
|
||||
.map(|s| s.to_string());
|
||||
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
|
||||
|
||||
+3
-2
@@ -127,7 +127,8 @@ pub async fn process_upload_media_file(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.as_ref()
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_file_size_bytes = upload_media_request.file_bytes
|
||||
.as_ref()
|
||||
@@ -325,7 +326,7 @@ pub async fn process_upload_media_file(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: None,
|
||||
media_file_type,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
|
||||
let mut field_name = None;
|
||||
let mut field_filename = None;
|
||||
|
||||
if let content_disposition = field.content_disposition() {
|
||||
if let Some(content_disposition) = field.content_disposition() {
|
||||
field_name = content_disposition.get_name()
|
||||
.map(|s| s.to_string());
|
||||
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
|
||||
|
||||
+5
-4
@@ -129,7 +129,8 @@ pub async fn handle_upload(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.as_ref()
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_file_size_bytes = upload_media_request.file_bytes
|
||||
.as_ref()
|
||||
@@ -300,7 +301,7 @@ pub async fn handle_upload(
|
||||
checksum_sha2: &hash,
|
||||
public_upload_path: &public_upload_path,
|
||||
maybe_extra_file_modification_info: None,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: None,
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
@@ -317,12 +318,12 @@ pub async fn handle_upload(
|
||||
let firehose_result = match media_upload_source {
|
||||
MediaUploadSource::DeviceApi => {
|
||||
server_state.firehose_publisher.publish_device_media_recorded(
|
||||
maybe_user_token.as_ref(),
|
||||
maybe_user_token,
|
||||
&token).await
|
||||
}
|
||||
_ => {
|
||||
server_state.firehose_publisher.publish_media_uploaded(
|
||||
maybe_user_token.as_ref(),
|
||||
maybe_user_token,
|
||||
&token).await
|
||||
}
|
||||
};
|
||||
|
||||
+3
-9
@@ -28,8 +28,7 @@ use crate::http_server::requests::request_headers::get_routing_tag_header::get_r
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::RequireUserSessionError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended_using_connection::require_user_session_extended_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended::require_user_session_extended;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::cleaners::empty_media_file_token_to_null::empty_media_file_token_to_null;
|
||||
|
||||
@@ -114,15 +113,10 @@ pub async fn enqueue_face_fusion_workflow_handler(
|
||||
let maybe_avt_token = server_state.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
let user_session = require_user_session_extended_using_connection(
|
||||
let user_session = require_user_session_extended(
|
||||
&http_request,
|
||||
&server_state.session_checker,
|
||||
&mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => CommonWebError::from_error(err),
|
||||
RequireUserSessionError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
&mut *mysql_connection).await?;
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
|
||||
|
||||
+3
-9
@@ -29,8 +29,7 @@ use crate::http_server::requests::request_headers::get_routing_tag_header::get_r
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::RequireUserSessionError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended_using_connection::require_user_session_extended_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended::require_user_session_extended;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::cleaners::empty_media_file_token_to_null::empty_media_file_token_to_null;
|
||||
|
||||
@@ -122,15 +121,10 @@ pub async fn enqueue_live_portrait_workflow_handler(
|
||||
let maybe_avt_token = server_state.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
let user_session = require_user_session_extended_using_connection(
|
||||
let user_session = require_user_session_extended(
|
||||
&http_request,
|
||||
&server_state.session_checker,
|
||||
&mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => CommonWebError::from_error(err),
|
||||
RequireUserSessionError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
&mut *mysql_connection).await?;
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
|
||||
|
||||
-272
@@ -1,272 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use actix::*;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{
|
||||
web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder,
|
||||
};
|
||||
use actix_web_actors::ws;
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::error;
|
||||
use redis::{Commands, RedisResult};
|
||||
use utoipa::ToSchema;
|
||||
use web::Data;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use mysql_queries::queries::generic_inference::web::get_inference_job_status::get_inference_job_status;
|
||||
use mysql_queries::queries::generic_inference::web::job_status::GenericInferenceJobStatus;
|
||||
use redis_schema::keys::inference_job::style_transfer_progress_key::StyleTransferProgressKey;
|
||||
use redis_schema::payloads::inference_job::style_transfer_progress_state::InferenceProgressDetailsResponse;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::{progress_tracker_server, progress_tracker_session};
|
||||
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
|
||||
use crate::http_server::endpoints::inference_job::utils::extractors::extract_polymorphic_inference_args::extract_polymorphic_inference_args;
|
||||
use crate::http_server::web_utils::filter_model_name::maybe_filter_model_name;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// For certain jobs or job classes (eg. non-premium), we kill the jobs if the user hasn't
|
||||
/// maintained a keepalive. This prevents wasted work when users who are unlikely to return
|
||||
/// navigate away. Premium users have accounts and can always return to the site, so they
|
||||
/// typically do not require keepalive.
|
||||
const JOB_KEEPALIVE_TTL_SECONDS : usize = 60 * 3;
|
||||
|
||||
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct GetInferenceJobStatusPathInfo {
|
||||
token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct GetInferenceJobStatusSuccessResponse {
|
||||
pub success: bool,
|
||||
pub state: InferenceJobStatusResponsePayload,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct InferenceJobStatusResponsePayload {
|
||||
pub job_token: InferenceJobToken,
|
||||
|
||||
pub request: RequestDetailsResponse,
|
||||
pub status: StatusDetailsResponse,
|
||||
pub maybe_result: Option<InferenceProgressDetailsResponse>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
|
||||
/// Details about what the user requested for generation
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct RequestDetailsResponse {
|
||||
}
|
||||
|
||||
/// Details about the ongoing job status
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct StatusDetailsResponse {
|
||||
/// Primary status from the database (a state machine).
|
||||
pub status: JobStatusPlus,
|
||||
|
||||
/// Extra, temporary status from Redis.
|
||||
/// This can denote inference progress, and the Python code can write to it.
|
||||
pub maybe_extra_status_description: Option<String>,
|
||||
|
||||
pub maybe_assigned_worker: Option<String>,
|
||||
pub maybe_assigned_cluster: Option<String>,
|
||||
|
||||
pub maybe_first_started_at: Option<DateTime<Utc>>,
|
||||
|
||||
pub attempt_count: u8,
|
||||
|
||||
/// Whether the frontend needs to maintain a keepalive check.
|
||||
/// This is typically only for non-premium users.
|
||||
pub requires_keepalive: bool,
|
||||
|
||||
/// An enum the frontend can use to display localized/I18N error
|
||||
/// messages. These pertain to both transient and permanent failures.
|
||||
pub maybe_failure_category: Option<FrontendFailureCategory>,
|
||||
|
||||
/// This is an integer number between 0 and 100 (both inclusive) that
|
||||
/// reports the completeness.
|
||||
pub progress_percentage: u8,
|
||||
}
|
||||
|
||||
// #[derive(Serialize, ToSchema)]
|
||||
// pub struct InferenceStageDetails {
|
||||
// pub stage_progress: u8,
|
||||
// pub expected_frame_count: u8,
|
||||
// pub stage_complete: bool,
|
||||
// pub frames: Vec<Url>,
|
||||
// }
|
||||
|
||||
/// Details about the completed result (if any)
|
||||
// #[derive(Serialize, ToSchema)]
|
||||
// pub struct InferenceProgressDetailsResponse {
|
||||
// pub expected_stages: u8,
|
||||
// pub currently_active_stage: u8,
|
||||
// pub per_stage_frame_count: u8,
|
||||
// pub stages: Vec<InferenceStageDetails>,
|
||||
// }
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum GetInferenceJobStatusError {
|
||||
ServerError,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl ResponseError for GetInferenceJobStatusError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
GetInferenceJobStatusError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
GetInferenceJobStatusError::NotFound => StatusCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
Self::ServerError => "server error".to_string(),
|
||||
Self::NotFound => "not found".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for GetInferenceJobStatusError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_inference_preview_status_handler_ws(
|
||||
http_request: HttpRequest,
|
||||
path: Path<GetInferenceJobStatusPathInfo>,
|
||||
server_state: Data<Arc<ServerState>>) -> Result<Json<GetInferenceJobStatusSuccessResponse>, GetInferenceJobStatusError>
|
||||
{
|
||||
if path.token.as_str().trim() == "None" {
|
||||
// NB: A bunch of Python clients use our API and can fail in this manner.
|
||||
// This was a large traffic driver during the 2023-03-08 outage.
|
||||
// Presumably it's this client: https://github.com/shards-7/fakeyou.py
|
||||
return Err(GetInferenceJobStatusError::NotFound);
|
||||
}
|
||||
|
||||
// NB: Since this is publicly exposed, we don't query sensitive data.
|
||||
let maybe_status = get_inference_job_status(
|
||||
&path.token,
|
||||
&server_state.mysql_pool
|
||||
).await;
|
||||
|
||||
let record = match maybe_status {
|
||||
Ok(Some(record)) => record,
|
||||
Ok(None) => return Err(GetInferenceJobStatusError::NotFound),
|
||||
Err(err) => {
|
||||
error!("tts job query error: {:?}", err);
|
||||
return Err(GetInferenceJobStatusError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
let mut redis = server_state.redis_pool
|
||||
.get()
|
||||
.map_err(|e| {
|
||||
error!("redis error: {:?}", e);
|
||||
GetInferenceJobStatusError::ServerError
|
||||
})?;
|
||||
|
||||
let progress_key = StyleTransferProgressKey::new_for_job_id(record.job_token.clone());
|
||||
let progress_value: RedisResult<Option<String>> = redis.get(progress_key.to_string());
|
||||
|
||||
let maybe_result = match progress_value {
|
||||
Ok(Some(value)) => {
|
||||
let progress_details: InferenceProgressDetailsResponse = serde_json::from_str(&value)
|
||||
.map_err(|e| {
|
||||
error!("redis error: {:?}", e);
|
||||
GetInferenceJobStatusError::ServerError
|
||||
})?;
|
||||
|
||||
Some(progress_details)
|
||||
},
|
||||
Ok(None) => None,
|
||||
Err(e) => {
|
||||
error!("redis error: {:?}", e);
|
||||
return Err(GetInferenceJobStatusError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Ok(Json(GetInferenceJobStatusSuccessResponse {
|
||||
success: true,
|
||||
state: record_to_payload(record, maybe_result, None),
|
||||
}))
|
||||
}
|
||||
|
||||
fn record_to_payload(
|
||||
record: GenericInferenceJobStatus,
|
||||
progress_details: Option<InferenceProgressDetailsResponse>,
|
||||
maybe_extra_status_description: Option<String>,
|
||||
) -> InferenceJobStatusResponsePayload {
|
||||
let inference_category = record.request_details.inference_category;
|
||||
|
||||
// NB: Fail open. We don't want to fail the request if we can't extract the args.
|
||||
let maybe_polymorphic_args = extract_polymorphic_inference_args(&record)
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
let progress_percentage = estimate_job_progress(&record, maybe_polymorphic_args.as_ref());
|
||||
|
||||
InferenceJobStatusResponsePayload {
|
||||
job_token: record.job_token,
|
||||
request: RequestDetailsResponse {
|
||||
// inference_category: record.request_details.inference_category,
|
||||
// maybe_model_type: maybe_filter_model_name(record.request_details.maybe_model_type.as_deref()),
|
||||
// maybe_model_token: record.request_details.maybe_model_token,
|
||||
// maybe_model_title: record.request_details.maybe_model_title,
|
||||
// maybe_raw_inference_text: record.request_details.maybe_raw_inference_text,
|
||||
// maybe_style_name: record.request_details.maybe_style_name,
|
||||
},
|
||||
status: StatusDetailsResponse {
|
||||
status: record.status,
|
||||
maybe_extra_status_description,
|
||||
maybe_assigned_worker: maybe_filter_model_name(record.maybe_assigned_worker.as_deref()),
|
||||
maybe_assigned_cluster: record.maybe_assigned_cluster,
|
||||
maybe_first_started_at: record.maybe_first_started_at,
|
||||
attempt_count: record.attempt_count as u8,
|
||||
requires_keepalive: record.is_keepalive_required,
|
||||
maybe_failure_category: record.maybe_frontend_failure_category,
|
||||
progress_percentage,
|
||||
},
|
||||
maybe_result: progress_details,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProgressTrackerState {
|
||||
|
||||
}
|
||||
pub async fn ws_progress_route(
|
||||
req: HttpRequest,
|
||||
stream: web::Payload,
|
||||
srv: web::Data<Addr<progress_tracker_server::ChatServer>>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
ws::start(
|
||||
progress_tracker_session::WsChatSession {
|
||||
id: 0,
|
||||
hb: Instant::now(),
|
||||
room: "main".to_owned(),
|
||||
name: None,
|
||||
addr: srv.get_ref().clone(),
|
||||
},
|
||||
&req,
|
||||
stream,
|
||||
)
|
||||
}
|
||||
-3
@@ -3,7 +3,4 @@ pub mod enqueue_live_portrait_workflow_handler;
|
||||
pub mod enqueue_studio_workflow_handler;
|
||||
pub mod enqueue_video_style_transfer_workflow_handler;
|
||||
pub mod get_inference_preview_status;
|
||||
pub mod get_inference_preview_status_ws;
|
||||
pub mod progress_tracker_server;
|
||||
pub mod progress_tracker_session;
|
||||
pub mod vst_common;
|
||||
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
//! `ChatServer` is an actor. It maintains a list of connection client sessions.
|
||||
//! It also manages available rooms. Peers send messages to other peers in same
|
||||
//! room through `ChatServer`.
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use actix::prelude::*;
|
||||
use log::info;
|
||||
use rand::{rngs::ThreadRng, Rng};
|
||||
|
||||
/// Chat server sends this messages to session
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct Message(pub String);
|
||||
|
||||
/// Message for chat server communications
|
||||
|
||||
/// New chat session is created
|
||||
#[derive(Message)]
|
||||
#[rtype(usize)]
|
||||
pub struct Connect {
|
||||
pub addr: Recipient<Message>,
|
||||
}
|
||||
|
||||
/// Session is disconnected
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct Disconnect {
|
||||
pub id: usize,
|
||||
}
|
||||
|
||||
/// Send message to specific room
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct ClientMessage {
|
||||
/// Id of the client session
|
||||
pub id: usize,
|
||||
/// Peer message
|
||||
pub msg: String,
|
||||
/// Room name
|
||||
pub room: String,
|
||||
}
|
||||
|
||||
/// List of available rooms
|
||||
pub struct ListRooms;
|
||||
|
||||
impl actix::Message for ListRooms {
|
||||
type Result = Vec<String>;
|
||||
}
|
||||
|
||||
/// Join room, if room does not exists create new one.
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct Join {
|
||||
/// Client ID
|
||||
pub id: usize,
|
||||
|
||||
/// Room name
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// `ChatServer` manages chat rooms and responsible for coordinating chat session.
|
||||
///
|
||||
/// Implementation is very naïve.
|
||||
#[derive(Debug)]
|
||||
pub struct ChatServer {
|
||||
sessions: HashMap<usize, Recipient<Message>>,
|
||||
rooms: HashMap<String, HashSet<usize>>,
|
||||
rng: ThreadRng,
|
||||
visitor_count: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
impl ChatServer {
|
||||
pub fn new(visitor_count: Arc<AtomicUsize>) -> ChatServer {
|
||||
// default room
|
||||
let mut rooms = HashMap::new();
|
||||
rooms.insert("main".to_owned(), HashSet::new());
|
||||
|
||||
ChatServer {
|
||||
sessions: HashMap::new(),
|
||||
rooms,
|
||||
rng: rand::thread_rng(),
|
||||
visitor_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChatServer {
|
||||
/// Send message to all users in the room
|
||||
fn send_message(&self, room: &str, message: &str, skip_id: usize) {
|
||||
if let Some(sessions) = self.rooms.get(room) {
|
||||
for id in sessions {
|
||||
if *id != skip_id {
|
||||
if let Some(addr) = self.sessions.get(id) {
|
||||
addr.do_send(Message(message.to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Make actor from `ChatServer`
|
||||
impl Actor for ChatServer {
|
||||
/// We are going to use simple Context, we just need ability to communicate
|
||||
/// with other actors.
|
||||
type Context = Context<Self>;
|
||||
}
|
||||
|
||||
/// Handler for Connect message.
|
||||
///
|
||||
/// Register new session and assign unique id to this session
|
||||
impl Handler<Connect> for ChatServer {
|
||||
type Result = usize;
|
||||
|
||||
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
|
||||
info!("Someone joined");
|
||||
|
||||
// notify all users in same room
|
||||
self.send_message("main", "Someone joined", 0);
|
||||
|
||||
// register session with random id
|
||||
let id = self.rng.random::<u64>() as usize;
|
||||
self.sessions.insert(id, msg.addr);
|
||||
|
||||
// auto join session to main room
|
||||
self.rooms.entry("main".to_owned()).or_default().insert(id);
|
||||
|
||||
let count = self.visitor_count.fetch_add(1, Ordering::SeqCst);
|
||||
self.send_message("main", &format!("Total visitors {count}"), 0);
|
||||
|
||||
// send id back
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for Disconnect message.
|
||||
impl Handler<Disconnect> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
|
||||
info!("Someone disconnected");
|
||||
|
||||
let mut rooms: Vec<String> = Vec::new();
|
||||
|
||||
// remove address
|
||||
if self.sessions.remove(&msg.id).is_some() {
|
||||
// remove session from all rooms
|
||||
for (name, sessions) in &mut self.rooms {
|
||||
if sessions.remove(&msg.id) {
|
||||
rooms.push(name.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
// send message to other users
|
||||
for room in rooms {
|
||||
self.send_message(&room, "Someone disconnected", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for Message message.
|
||||
impl Handler<ClientMessage> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: ClientMessage, _: &mut Context<Self>) {
|
||||
self.send_message(&msg.room, msg.msg.as_str(), msg.id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for `ListRooms` message.
|
||||
impl Handler<ListRooms> for ChatServer {
|
||||
type Result = MessageResult<ListRooms>;
|
||||
|
||||
fn handle(&mut self, _: ListRooms, _: &mut Context<Self>) -> Self::Result {
|
||||
let mut rooms = Vec::new();
|
||||
|
||||
for key in self.rooms.keys() {
|
||||
rooms.push(key.to_owned())
|
||||
}
|
||||
|
||||
MessageResult(rooms)
|
||||
}
|
||||
}
|
||||
|
||||
/// Join room, send disconnect message to old room
|
||||
/// send join message to new room
|
||||
impl Handler<Join> for ChatServer {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: Join, _: &mut Context<Self>) {
|
||||
let Join { id, name } = msg;
|
||||
let mut rooms = Vec::new();
|
||||
|
||||
// remove session from all rooms
|
||||
for (n, sessions) in &mut self.rooms {
|
||||
if sessions.remove(&id) {
|
||||
rooms.push(n.to_owned());
|
||||
}
|
||||
}
|
||||
// send message to other users
|
||||
for room in rooms {
|
||||
self.send_message(&room, "Someone disconnected", 0);
|
||||
}
|
||||
|
||||
self.rooms.entry(name.clone()).or_default().insert(id);
|
||||
|
||||
self.send_message(&name, "Someone connected", id);
|
||||
}
|
||||
}
|
||||
-205
@@ -1,205 +0,0 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use actix::prelude::*;
|
||||
use actix_web_actors::ws;
|
||||
use log::{debug, info, warn};
|
||||
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::progress_tracker_server;
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::progress_tracker_server::ChatServer;
|
||||
|
||||
/// How often heartbeat pings are sent
|
||||
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||||
|
||||
/// How long before lack of client response causes a timeout
|
||||
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WsChatSession {
|
||||
/// unique session id
|
||||
pub id: usize,
|
||||
|
||||
/// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
|
||||
/// otherwise we drop connection.
|
||||
pub hb: Instant,
|
||||
|
||||
/// joined room
|
||||
pub room: String,
|
||||
|
||||
/// peer name
|
||||
pub name: Option<String>,
|
||||
|
||||
/// Chat server
|
||||
pub addr: Addr<ChatServer>,
|
||||
}
|
||||
|
||||
impl WsChatSession {
|
||||
/// helper method that sends ping to client every 5 seconds (HEARTBEAT_INTERVAL).
|
||||
///
|
||||
/// also this method checks heartbeats from client
|
||||
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
|
||||
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
||||
// check client heartbeats
|
||||
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
|
||||
// heartbeat timed out
|
||||
warn!("Websocket Client heartbeat failed, disconnecting!");
|
||||
|
||||
// notify chat server
|
||||
act.addr.do_send(progress_tracker_server::Disconnect { id: act.id });
|
||||
|
||||
// stop actor
|
||||
ctx.stop();
|
||||
|
||||
// don't try to send a ping
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.ping(b"");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for WsChatSession {
|
||||
type Context = ws::WebsocketContext<Self>;
|
||||
|
||||
/// Method is called on actor start.
|
||||
/// We register ws session with ChatServer
|
||||
fn started(&mut self, ctx: &mut Self::Context) {
|
||||
// we'll start heartbeat process on session start.
|
||||
self.hb(ctx);
|
||||
|
||||
// register self in chat server. `AsyncContext::wait` register
|
||||
// future within context, but context waits until this future resolves
|
||||
// before processing any other events.
|
||||
// HttpContext::state() is instance of WsChatSessionState, state is shared
|
||||
// across all routes within application
|
||||
let addr = ctx.address();
|
||||
self.addr
|
||||
.send(progress_tracker_server::Connect {
|
||||
addr: addr.recipient(),
|
||||
})
|
||||
.into_actor(self)
|
||||
.then(|res, act, ctx| {
|
||||
match res {
|
||||
Ok(res) => act.id = res,
|
||||
// something is wrong with chat server
|
||||
_ => ctx.stop(),
|
||||
}
|
||||
fut::ready(())
|
||||
})
|
||||
.wait(ctx);
|
||||
}
|
||||
|
||||
fn stopping(&mut self, _: &mut Self::Context) -> Running {
|
||||
// notify chat server
|
||||
self.addr.do_send(progress_tracker_server::Disconnect { id: self.id });
|
||||
Running::Stop
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle messages from chat server, we simply send it to peer websocket
|
||||
impl Handler<progress_tracker_server::Message> for WsChatSession {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: progress_tracker_server::Message, ctx: &mut Self::Context) {
|
||||
ctx.text(msg.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// WebSocket message handler
|
||||
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsChatSession {
|
||||
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
||||
log::debug!("WEBSOCKET MESSAGE: {msg:?}");
|
||||
let msg = match msg {
|
||||
Err(_) => {
|
||||
ctx.stop();
|
||||
return;
|
||||
}
|
||||
Ok(msg) => msg,
|
||||
};
|
||||
|
||||
log::debug!("WEBSOCKET MESSAGE: {msg:?}");
|
||||
match msg {
|
||||
ws::Message::Ping(msg) => {
|
||||
self.hb = Instant::now();
|
||||
ctx.pong(&msg);
|
||||
}
|
||||
ws::Message::Pong(_) => {
|
||||
self.hb = Instant::now();
|
||||
}
|
||||
ws::Message::Text(text) => {
|
||||
let m = text.trim();
|
||||
// we check for /sss type of messages
|
||||
if m.starts_with('/') {
|
||||
let v: Vec<&str> = m.splitn(2, ' ').collect();
|
||||
match v[0] {
|
||||
"/list" => {
|
||||
// Send ListRooms message to chat server and wait for
|
||||
// response
|
||||
debug!("List rooms");
|
||||
self.addr
|
||||
.send(progress_tracker_server::ListRooms)
|
||||
.into_actor(self)
|
||||
.then(|res, _, ctx| {
|
||||
match res {
|
||||
Ok(rooms) => {
|
||||
for room in rooms {
|
||||
ctx.text(room);
|
||||
}
|
||||
}
|
||||
_ => warn!("Something is wrong"),
|
||||
}
|
||||
fut::ready(())
|
||||
})
|
||||
.wait(ctx)
|
||||
// .wait(ctx) pauses all events in context,
|
||||
// so actor wont receive any new messages until it get list
|
||||
// of rooms back
|
||||
}
|
||||
"/join" => {
|
||||
if v.len() == 2 {
|
||||
v[1].clone_into(&mut self.room);
|
||||
self.addr.do_send(progress_tracker_server::Join {
|
||||
id: self.id,
|
||||
name: self.room.clone(),
|
||||
});
|
||||
|
||||
ctx.text("joined");
|
||||
} else {
|
||||
ctx.text("!!! room name is required");
|
||||
}
|
||||
}
|
||||
"/name" => {
|
||||
if v.len() == 2 {
|
||||
self.name = Some(v[1].to_owned());
|
||||
} else {
|
||||
ctx.text("!!! name is required");
|
||||
}
|
||||
}
|
||||
_ => ctx.text(format!("!!! unknown command: {m:?}")),
|
||||
}
|
||||
} else {
|
||||
let msg = if let Some(ref name) = self.name {
|
||||
format!("{name}: {m}")
|
||||
} else {
|
||||
m.to_owned()
|
||||
};
|
||||
// send message to chat server
|
||||
self.addr.do_send(progress_tracker_server::ClientMessage {
|
||||
id: self.id,
|
||||
msg,
|
||||
room: self.room.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
ws::Message::Binary(_) => warn!("Unexpected binary"),
|
||||
ws::Message::Close(reason) => {
|
||||
ctx.close(reason);
|
||||
ctx.stop();
|
||||
}
|
||||
ws::Message::Continuation(_) => {
|
||||
ctx.stop();
|
||||
}
|
||||
ws::Message::Nop => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-7
@@ -14,7 +14,7 @@ use mysql_queries::queries::users::user_profiles::get_user_profile_by_username::
|
||||
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, RequireModeratorError, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAXIMUM_KEYS : u32 = 100;
|
||||
@@ -60,12 +60,7 @@ pub async fn create_beta_keys_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => CommonWebError::from_error(err),
|
||||
RequireModeratorError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let mut maybe_referrer_user_token = None;
|
||||
|
||||
|
||||
+2
-7
@@ -14,7 +14,7 @@ use tokens::tokens::w2l_results::W2lResultToken;
|
||||
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, RequireModeratorError, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// For the URL PathInfo
|
||||
@@ -58,12 +58,7 @@ pub async fn edit_beta_key_distributed_flag_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => CommonWebError::from_error(err),
|
||||
RequireModeratorError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
edit_beta_key_distributed_flag(&path.token, request.is_distributed, &server_state.mysql_pool)
|
||||
.await
|
||||
|
||||
+2
-7
@@ -13,7 +13,7 @@ use tokens::tokens::beta_keys::BetaKeyToken;
|
||||
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, RequireModeratorError, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// For the URL PathInfo
|
||||
@@ -57,12 +57,7 @@ pub async fn edit_beta_key_note_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => CommonWebError::from_error(err),
|
||||
RequireModeratorError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let maybe_note = request.note.as_deref()
|
||||
.map(|note| note.trim())
|
||||
|
||||
+3
-7
@@ -17,7 +17,7 @@ use tokens::tokens::beta_keys::BetaKeyToken;
|
||||
use crate::http_server::common_responses::pagination_page::PaginationPage;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::user_details_lite::UserDetailsLight;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::{require_user_session, RequireUserSessionError};
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[derive(Copy, Clone, Deserialize, ToSchema)]
|
||||
@@ -108,12 +108,8 @@ pub async fn list_beta_keys_handler(
|
||||
query: Query<ListBetaKeysQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
let user_session = require_user_session(&http_request, &server_state)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => CommonWebError::from_error(err),
|
||||
RequireUserSessionError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
let mut is_mod = user_session.can_ban_users;
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ pub async fn redeem_beta_key_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let user_session = require_user_session(&http_request, &server_state).await?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let rate_limiter = &server_state.redis_rate_limiters.logged_out;
|
||||
|
||||
|
||||
+2
-6
@@ -10,7 +10,7 @@ use mysql_queries::queries::characters::delete_character::delete_character;
|
||||
use mysql_queries::queries::characters::get_character_by_token_including_deleted::get_character_by_token_including_deleted;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Delete a character.
|
||||
@@ -40,11 +40,7 @@ pub async fn delete_character_handler(
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request,
|
||||
&server_state.session_checker,
|
||||
&mut mysql_connection,
|
||||
).await?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *mysql_connection).await?;
|
||||
|
||||
let user_token = &user_session.user_token;
|
||||
let is_mod = user_session.is_mod();
|
||||
|
||||
+2
-6
@@ -11,7 +11,7 @@ use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use seedance2pro_client::requests::update_character::update_character::{update_character, UpdateCharacterArgs};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Edit a character's name or description.
|
||||
@@ -38,11 +38,7 @@ pub async fn edit_character_handler(
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request,
|
||||
&server_state.session_checker,
|
||||
&mut mysql_connection,
|
||||
).await?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *mysql_connection).await?;
|
||||
|
||||
let user_token = &user_session.user_token;
|
||||
let is_mod = user_session.is_mod();
|
||||
|
||||
+2
-6
@@ -16,7 +16,7 @@ use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// List characters for the current session.
|
||||
@@ -39,11 +39,7 @@ pub async fn list_characters_handler(
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request,
|
||||
&server_state.session_checker,
|
||||
&mut mysql_connection,
|
||||
).await?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *mysql_connection).await?;
|
||||
|
||||
let user_token = &user_session.user_token;
|
||||
|
||||
|
||||
+2
-4
@@ -17,7 +17,7 @@ use mysql_queries::queries::folders::folder::update_folder_color_code::{
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_COLOR_CODE_LEN: usize = 16;
|
||||
@@ -47,9 +47,7 @@ pub async fn color_code_folder_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
// Normalize: trim, treat empty after trim as `None`. This lets clients
|
||||
// clear by passing `""` or by omitting the field entirely.
|
||||
|
||||
+2
-4
@@ -23,7 +23,7 @@ use tokens::tokens::folders::FolderToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Set or clear the custom cover image on a folder.
|
||||
@@ -64,9 +64,7 @@ pub async fn cover_image_folder_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
// Confirm the folder exists + is owned. Authoritative 404 source.
|
||||
get_folder_for_owner(GetFolderForOwnerArgs {
|
||||
|
||||
+2
-4
@@ -17,7 +17,7 @@ use crate::http_server::endpoints::folders::folder::folder_info_conversion::{
|
||||
build_folder_thumbnails_lookup, folder_row_to_info,
|
||||
};
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_NAME_LEN: usize = 255;
|
||||
@@ -45,9 +45,7 @@ pub async fn create_folder_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
let name = request.name.trim().to_string();
|
||||
|
||||
|
||||
+2
-4
@@ -12,7 +12,7 @@ use mysql_queries::queries::folders::folder::soft_delete_folder::{
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Soft-delete a folder. Children retain their parent pointer and become
|
||||
@@ -39,9 +39,7 @@ pub async fn delete_folder_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
|
||||
let rows_affected = soft_delete_folder(SoftDeleteFolderArgs {
|
||||
|
||||
+2
-4
@@ -16,7 +16,7 @@ use crate::http_server::endpoints::folders::folder::folder_info_conversion::{
|
||||
build_folder_thumbnails_lookup, folder_row_to_info,
|
||||
};
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Fetch a single folder owned by the logged-in user.
|
||||
@@ -42,9 +42,7 @@ pub async fn get_folder_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
let row = get_folder_for_owner(GetFolderForOwnerArgs {
|
||||
folder_token: &path.folder_token,
|
||||
|
||||
+2
-4
@@ -17,7 +17,7 @@ use crate::http_server::endpoints::folders::folder::folder_info_conversion::{
|
||||
build_folder_thumbnails_lookup, folder_row_to_info,
|
||||
};
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "folders_all";
|
||||
@@ -48,9 +48,7 @@ pub async fn list_folders_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
let limit = query.limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT);
|
||||
|
||||
|
||||
+2
-4
@@ -17,7 +17,7 @@ use mysql_queries::queries::folders::folder::update_folder_name::{
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_NAME_LEN: usize = 255;
|
||||
@@ -47,9 +47,7 @@ pub async fn rename_folder_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
let new_name = request.new_name.trim().to_string();
|
||||
|
||||
|
||||
+2
-4
@@ -17,7 +17,7 @@ use mysql_queries::queries::folders::folder::update_folder_has_star::{
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -44,9 +44,7 @@ pub async fn star_folder_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
// Confirm the folder exists + is owned before issuing the update.
|
||||
// A 404 here is the authoritative "not found" signal — the update
|
||||
|
||||
+2
-4
@@ -27,7 +27,7 @@ use mysql_queries::queries::folders::media_files::recompute_folder_last_media_fi
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_BULK: usize = 500;
|
||||
@@ -61,9 +61,7 @@ pub async fn bulk_add_folder_media_files_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
if request.media_file_tokens.len() > MAX_BULK {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
|
||||
+2
-4
@@ -30,7 +30,7 @@ use mysql_queries::queries::folders::media_files::recompute_folder_last_media_fi
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_BULK: usize = 500;
|
||||
@@ -72,9 +72,7 @@ pub async fn bulk_move_folder_media_files_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
if request.media_file_tokens.len() > MAX_BULK {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
|
||||
+2
-4
@@ -24,7 +24,7 @@ use tokens::tokens::folders::FolderToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_BULK: usize = 500;
|
||||
@@ -56,9 +56,7 @@ pub async fn bulk_remove_folder_media_files_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
if request.media_file_tokens.len() > MAX_BULK {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
|
||||
+2
-4
@@ -32,7 +32,7 @@ use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "folder_mf";
|
||||
@@ -127,9 +127,7 @@ pub async fn list_folder_media_files_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
let folder = get_folder_for_owner(GetFolderForOwnerArgs {
|
||||
folder_token: &path.folder_token,
|
||||
|
||||
+2
-4
@@ -25,7 +25,7 @@ use mysql_queries::queries::folders::subfolder::filter_existing_owned_folder_tok
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_BULK: usize = 500;
|
||||
@@ -58,9 +58,7 @@ pub async fn bulk_add_subfolders_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
if request.subfolder_tokens.len() > MAX_BULK {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
|
||||
+2
-4
@@ -14,7 +14,7 @@ use mysql_queries::queries::folders::subfolder::bulk_clear_parent_folder::{
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const MAX_BULK: usize = 500;
|
||||
@@ -45,9 +45,7 @@ pub async fn bulk_remove_subfolders_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
if request.subfolder_tokens.len() > MAX_BULK {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
|
||||
+2
-4
@@ -21,7 +21,7 @@ use crate::http_server::endpoints::folders::folder::folder_info_conversion::{
|
||||
build_folder_thumbnails_lookup, folder_row_to_info,
|
||||
};
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "subfolders";
|
||||
@@ -55,9 +55,7 @@ pub async fn list_subfolders_handler(
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut conn,
|
||||
).await.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *conn).await?;
|
||||
|
||||
|
||||
// Confirm the parent exists + is owned by the caller before listing.
|
||||
|
||||
+2
-2
@@ -119,7 +119,7 @@ pub async fn upload_snapshot_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -247,7 +247,7 @@ pub async fn upload_snapshot_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Image),
|
||||
media_file_type,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+2
-3
@@ -8,7 +8,7 @@ use utoipa::ToSchema;
|
||||
use mysql_queries::queries::generic_inference::web::dismiss_finished_jobs_for_user::dismiss_finished_jobs_for_user;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
@@ -40,8 +40,7 @@ pub async fn dismiss_finished_session_jobs_handler(
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
&http_request, &server_state.session_checker, &mut mysql_connection)
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &mut *mysql_connection)
|
||||
.await?;
|
||||
|
||||
dismiss_finished_jobs_for_user(&mut mysql_connection, &user_session.user_token)
|
||||
|
||||
+2
-6
@@ -56,12 +56,8 @@ pub async fn change_media_file_animation_type_handler(
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<SimpleResponse>, CommonWebError> {
|
||||
|
||||
let user_session = require_user_session(&http_request, &server_state)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Not authorized: {:?}", e);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
let media_file_token = path.token.clone();
|
||||
let is_mod = user_session.is_mod();
|
||||
|
||||
+2
-6
@@ -53,12 +53,8 @@ pub async fn change_media_file_engine_category_handler(
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<SimpleResponse>, CommonWebError> {
|
||||
|
||||
let user_session = require_user_session(&http_request, &server_state)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Not authorized: {:?}", e);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_user_session(&http_request, &server_state.session_checker, &server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
let media_file_token = path.token.clone();
|
||||
let is_mod = user_session.is_mod();
|
||||
|
||||
+2
-2
@@ -135,7 +135,7 @@ pub async fn upload_audio_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -334,7 +334,7 @@ pub async fn upload_audio_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Audio),
|
||||
media_file_type: MediaFileType::Audio,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
|
||||
let mut field_name = None;
|
||||
let mut field_filename = None;
|
||||
|
||||
if let content_disposition = field.content_disposition() {
|
||||
if let Some(content_disposition) = field.content_disposition() {
|
||||
field_name = content_disposition.get_name()
|
||||
.map(|s| s.to_string());
|
||||
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
|
||||
|
||||
+3
-2
@@ -140,7 +140,8 @@ pub async fn upload_engine_asset_media_file_handler(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.as_ref()
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
// ==================== FILE DATA ==================== //
|
||||
|
||||
@@ -205,7 +206,7 @@ pub async fn upload_engine_asset_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Dimensional),
|
||||
media_file_type,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
|
||||
let mut field_name = None;
|
||||
let mut field_filename = None;
|
||||
|
||||
if let content_disposition = field.content_disposition() {
|
||||
if let Some(content_disposition) = field.content_disposition() {
|
||||
field_name = content_disposition.get_name()
|
||||
.map(|s| s.to_string());
|
||||
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
|
||||
|
||||
+4
-3
@@ -142,7 +142,8 @@ pub async fn process_upload_media_file(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.as_ref()
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_file_size_bytes = upload_media_request.file_bytes
|
||||
.as_ref()
|
||||
@@ -392,7 +393,7 @@ pub async fn process_upload_media_file(
|
||||
})?;
|
||||
|
||||
let media_token = MediaFileInsertBuilder::new()
|
||||
.maybe_creator_user(maybe_user_token.as_ref())
|
||||
.maybe_creator_user(maybe_user_token)
|
||||
.maybe_creator_anonymous_visitor(maybe_avt_token.as_ref())
|
||||
.creator_ip_address(&ip_address)
|
||||
.public_bucket_directory_hash(&public_upload_path)
|
||||
@@ -417,7 +418,7 @@ pub async fn process_upload_media_file(
|
||||
//let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
// maybe_media_class: Some(media_file_class), // DONE
|
||||
// media_file_type, // DONE
|
||||
// maybe_creator_user_token: maybe_user_token.as_ref(), // DONE
|
||||
// maybe_creator_user_token: maybe_user_token, // DONE
|
||||
// maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(), // DONE
|
||||
// creator_ip_address: &ip_address, // DONE
|
||||
// creator_set_visibility, // DONE
|
||||
|
||||
+2
-2
@@ -145,7 +145,7 @@ pub async fn upload_image_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -310,7 +310,7 @@ pub async fn upload_image_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Image),
|
||||
media_file_type,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+2
-2
@@ -179,7 +179,7 @@ pub async fn upload_new_engine_asset_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
// ==================== FILE DATA ==================== //
|
||||
|
||||
@@ -211,7 +211,7 @@ pub async fn upload_new_engine_asset_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(file_info.media_class),
|
||||
media_file_type: file_info.media_type,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: file_info.creator_set_visibility,
|
||||
|
||||
+2
-2
@@ -112,7 +112,7 @@ pub async fn upload_new_scene_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -226,7 +226,7 @@ pub async fn upload_new_scene_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Dimensional),
|
||||
media_file_type: MediaFileType::SceneJson,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+7
-6
@@ -23,7 +23,8 @@ use tokens::tokens::media_files::MediaFileToken;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_error::MediaFileUploadError;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_pmx::extract_and_upload_pmx_files::{extract_and_upload_pmx_files, PmxError};
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, RequireModeratorError, UseDatabase};
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Form-multipart request fields.
|
||||
@@ -115,11 +116,11 @@ pub async fn upload_pmx_media_file_handler(
|
||||
// ==================== READ SESSION ==================== //
|
||||
|
||||
// NB: We require a moderator to upload PMX files.
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::FromPool(&mut mysql_connection))
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => MediaFileUploadError::ServerError,
|
||||
RequireModeratorError::NotAuthorized => MediaFileUploadError::NotAuthorized,
|
||||
CommonWebError::NotAuthorized => MediaFileUploadError::NotAuthorized,
|
||||
_ => MediaFileUploadError::ServerError,
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -206,7 +207,7 @@ pub async fn upload_pmx_media_file_handler(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
//let maybe_user_token = maybe_user_session
|
||||
// .map(|session| session.get_strongly_typed_user_token());
|
||||
// .map(|session| session.get_user_token());
|
||||
|
||||
// ==================== FILE DATA ==================== //
|
||||
|
||||
@@ -257,7 +258,7 @@ pub async fn upload_pmx_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Dimensional),
|
||||
media_file_type: MediaFileType::Pmx,
|
||||
maybe_creator_user_token: Some(&user_session.get_strongly_typed_user_token()),
|
||||
maybe_creator_user_token: Some(user_session.get_user_token()),
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+2
-2
@@ -109,7 +109,7 @@ pub async fn upload_saved_scene_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -148,7 +148,7 @@ pub async fn upload_saved_scene_media_file_handler(
|
||||
|
||||
let creator_check = check_creator_tokens(CheckCreatorTokenArgs {
|
||||
maybe_creator_user_token: media_file.maybe_creator_user_token.as_ref(),
|
||||
maybe_current_request_user_token: maybe_user_token.as_ref(),
|
||||
maybe_current_request_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: media_file.maybe_creator_anonymous_visitor_token.as_ref(),
|
||||
maybe_current_request_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
});
|
||||
|
||||
+2
-2
@@ -111,7 +111,7 @@ pub async fn upload_scene_snapshot_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -219,7 +219,7 @@ pub async fn upload_scene_snapshot_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Dimensional),
|
||||
media_file_type: MediaFileType::SceneJson,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: Visibility::Hidden,
|
||||
|
||||
+2
-2
@@ -119,7 +119,7 @@ pub async fn upload_spz_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -255,7 +255,7 @@ pub async fn upload_spz_media_file_handler(
|
||||
let media_token = MediaFileInsertBuilder::new()
|
||||
.media_file_class(MediaFileClass::Dimensional)
|
||||
.media_file_type(MediaFileType::Spz)
|
||||
.maybe_creator_user(maybe_user_token.as_ref())
|
||||
.maybe_creator_user(maybe_user_token)
|
||||
.maybe_creator_anonymous_visitor(maybe_avt_token.as_ref())
|
||||
.creator_ip_address(&ip_address)
|
||||
.creator_set_visibility(creator_set_visibility)
|
||||
|
||||
+8
-5
@@ -19,13 +19,13 @@ use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token;
|
||||
use mysql_queries::queries::media_files::create::insert_media_file_from_studio_scene_render::{insert_media_file_from_studio_scene_render, InsertStudioSceneRenderArgs};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_error::MediaFileUploadError;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_studio_shot::extract_frames_from_zip::{extract_frames_from_zip, ExtractFramesError};
|
||||
use crate::http_server::endpoints::media_files::upload::upload_studio_shot::ffmpeg_frames_to_mp4::ffmpeg_frames_to_mp4;
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended_using_connection::require_user_session_extended_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended::require_user_session_extended;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const DEFAULT_FPS : u8 = 12;
|
||||
@@ -111,11 +111,14 @@ pub async fn upload_studio_shot_media_file_handler(
|
||||
// ==================== READ SESSION ==================== //
|
||||
|
||||
// NB: We require a moderator to upload PMX files.
|
||||
let user_session = require_user_session_extended_using_connection(&http_request, &server_state.session_checker, &mut mysql_connection)
|
||||
let user_session = require_user_session_extended(&http_request, &server_state.session_checker, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("User session error: {:?}", e);
|
||||
MediaFileUploadError::NotAuthorized
|
||||
match e {
|
||||
CommonWebError::NotAuthorized => MediaFileUploadError::NotAuthorized,
|
||||
_ => MediaFileUploadError::ServerError,
|
||||
}
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -176,7 +179,7 @@ pub async fn upload_studio_shot_media_file_handler(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
//let maybe_user_token = maybe_user_session
|
||||
// .map(|session| session.get_strongly_typed_user_token());
|
||||
// .map(|session| session.get_user_token());
|
||||
|
||||
// ==================== FILE DATA ==================== //
|
||||
|
||||
|
||||
+2
-2
@@ -177,7 +177,7 @@ pub async fn upload_new_video_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -507,7 +507,7 @@ pub async fn upload_new_video_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Video),
|
||||
media_file_type: MediaFileType::Video,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
|
||||
let mut field_name = None;
|
||||
let mut field_filename = None;
|
||||
|
||||
if let content_disposition = field.content_disposition() {
|
||||
if let Some(content_disposition) = field.content_disposition() {
|
||||
field_name = content_disposition.get_name()
|
||||
.map(|s| s.to_string());
|
||||
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
|
||||
|
||||
+3
-2
@@ -158,7 +158,8 @@ pub async fn upload_video_media_file_handler(
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.as_ref()
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
// ==================== FILE DATA ==================== //
|
||||
|
||||
@@ -221,7 +222,7 @@ pub async fn upload_video_media_file_handler(
|
||||
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
|
||||
maybe_media_class: Some(MediaFileClass::Video),
|
||||
media_file_type: MediaFileType::Video,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Result
|
||||
let mut field_name = None;
|
||||
let mut field_filename = None;
|
||||
|
||||
if let content_disposition = field.content_disposition() {
|
||||
if let Some(content_disposition) = field.content_disposition() {
|
||||
field_name = content_disposition.get_name()
|
||||
.map(|s| s.to_string());
|
||||
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
|
||||
|
||||
+3
-3
@@ -73,7 +73,7 @@ pub async fn write_engine_asset_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -117,7 +117,7 @@ pub async fn write_engine_asset_media_file_handler(
|
||||
|
||||
let creator_check = check_creator_tokens(CheckCreatorTokenArgs {
|
||||
maybe_creator_user_token: media_file.maybe_creator_user_token.as_ref(),
|
||||
maybe_current_request_user_token: maybe_user_token.as_ref(),
|
||||
maybe_current_request_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: media_file.maybe_creator_anonymous_visitor_token.as_ref(),
|
||||
maybe_current_request_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
});
|
||||
@@ -232,7 +232,7 @@ pub async fn write_engine_asset_media_file_handler(
|
||||
maybe_engine_category: upload_media_request.maybe_engine_category,
|
||||
maybe_animation_type: upload_media_request.maybe_animation_type,
|
||||
maybe_media_subtype: None,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Result
|
||||
let mut field_name = None;
|
||||
let mut field_filename = None;
|
||||
|
||||
if let content_disposition = field.content_disposition() {
|
||||
if let Some(content_disposition) = field.content_disposition() {
|
||||
field_name = content_disposition.get_name()
|
||||
.map(|s| s.to_string());
|
||||
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
|
||||
|
||||
+3
-3
@@ -75,7 +75,7 @@ pub async fn write_scene_file_media_file_handler(
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
.as_ref()
|
||||
.map(|session| session.get_strongly_typed_user_token());
|
||||
.map(|session| session.get_user_token());
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
@@ -119,7 +119,7 @@ pub async fn write_scene_file_media_file_handler(
|
||||
|
||||
let creator_check = check_creator_tokens(CheckCreatorTokenArgs {
|
||||
maybe_creator_user_token: media_file.maybe_creator_user_token.as_ref(),
|
||||
maybe_current_request_user_token: maybe_user_token.as_ref(),
|
||||
maybe_current_request_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: media_file.maybe_creator_anonymous_visitor_token.as_ref(),
|
||||
maybe_current_request_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
});
|
||||
@@ -220,7 +220,7 @@ pub async fn write_scene_file_media_file_handler(
|
||||
maybe_engine_category: Some(MediaFileEngineCategory::Scene),
|
||||
maybe_animation_type: None,
|
||||
maybe_media_subtype: None,
|
||||
maybe_creator_user_token: maybe_user_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_token,
|
||||
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility,
|
||||
|
||||
+7
-9
@@ -28,8 +28,7 @@ use crate::http_server::requests::request_headers::get_routing_tag_header::get_r
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::validations::validate_model_title::validate_model_title;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::RequireUserSessionError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended_using_connection::require_user_session_extended_using_connection;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_extended::require_user_session_extended;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
@@ -82,15 +81,14 @@ pub async fn enqueue_gptsovits_model_download_handler(
|
||||
let maybe_avt_token = server_state.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
let user_session = require_user_session_extended_using_connection(
|
||||
let user_session = require_user_session_extended(
|
||||
&http_request,
|
||||
&server_state.session_checker,
|
||||
&mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => CommonWebError::from_error(err),
|
||||
RequireUserSessionError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
&mut *mysql_connection).await?;
|
||||
|
||||
// NB: The connection isn't needed past this point (the enqueue below uses the pool directly),
|
||||
// so release it now rather than holding a pool slot for the rest of the handler.
|
||||
drop(mysql_connection);
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
|
||||
|
||||
+2
-7
@@ -14,7 +14,7 @@ use mysql_queries::queries::staff_audit_logs::insert_staff_audit_log::{
|
||||
use pager::notification::notification_details_builder::NotificationDetailsBuilder;
|
||||
use pager::notification::notification_urgency::NotificationUrgency;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Send a test alert via the pager system. Moderators only.
|
||||
@@ -35,12 +35,7 @@ pub async fn moderation_send_alert_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModerationSendAlertResponse>, CommonWebError> {
|
||||
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
|
||||
+2
-11
@@ -15,9 +15,7 @@ use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// ── Path params ──
|
||||
@@ -74,14 +72,7 @@ pub async fn moderation_list_debug_logs_for_token_handler(
|
||||
query: web::Query<ListDebugLogsQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListDebugLogsSuccessResponse>, CommonWebError> {
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
|
||||
+2
-7
@@ -25,7 +25,7 @@ use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::web_utils::serialize_as_json_error::serialize_as_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, RequireModeratorError, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// For the URL PathInfo
|
||||
@@ -60,12 +60,7 @@ pub async fn moderator_get_token_info_handler(
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => CommonWebError::from_error(err),
|
||||
RequireModeratorError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let token = path.token.trim();
|
||||
let maybe_result = get_entity_from_token(&server_state.mysql_pool, &path.token)
|
||||
|
||||
+2
-11
@@ -16,9 +16,7 @@ use tokens::tokens::prompts::PromptToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// ── Path params ──
|
||||
@@ -92,14 +90,7 @@ pub async fn moderation_get_job_by_token_handler(
|
||||
path: web::Path<GetJobByTokenPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<GetJobByTokenSuccessResponse>, CommonWebError> {
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
|
||||
+2
-4
@@ -13,7 +13,7 @@ use mysql_queries::queries::generic_inference::web::list_user_jobs_for_moderatio
|
||||
|
||||
use tokens::tokens::users::UserToken;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// List jobs for a user (moderation)
|
||||
@@ -36,9 +36,7 @@ pub async fn list_user_jobs_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListUserJobsResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let results = list_user_jobs_for_moderation(&path.user_token, &server_state.mysql_pool)
|
||||
.await
|
||||
|
||||
+2
-11
@@ -13,9 +13,7 @@ use tokens::tokens::staff_audit_logs::StaffAuditLogToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "modstfaud";
|
||||
@@ -76,14 +74,7 @@ pub async fn moderator_list_staff_audit_logs_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListStaffAuditLogsSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let limit = query.limit
|
||||
.unwrap_or(DEFAULT_LIMIT)
|
||||
|
||||
+2
-4
@@ -15,7 +15,7 @@ use mysql_queries::queries::users::user::list::list_subscribing_users_by_signup_
|
||||
};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// List subscribing users by signup date (descending), for moderators.
|
||||
@@ -36,9 +36,7 @@ pub async fn moderator_list_subscribing_users_by_signup_date_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorListSubscribingUsersBySignupDateResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let result = list_subscribing_users_by_signup_date_for_moderation(
|
||||
ListSubscribingUsersBySignupDateArgs {
|
||||
|
||||
+2
-4
@@ -15,7 +15,7 @@ use mysql_queries::queries::users::user::list::list_users_by_signup_date_for_mod
|
||||
};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// List users by signup date (descending), for moderators.
|
||||
@@ -36,9 +36,7 @@ pub async fn moderator_list_users_by_signup_date_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorListUsersBySignupDateResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let result = list_users_by_signup_date_for_moderation(
|
||||
ListUsersBySignupDateArgs {
|
||||
|
||||
+2
-4
@@ -12,7 +12,7 @@ use artcraft_api_defs::moderation::user::user_lookup_by_stripe_customer_id::{
|
||||
use mysql_queries::queries::users::user_subscriptions::lookup_users_by_stripe_customer_id::lookup_users_by_stripe_customer_id;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Moderator User Lookup by Stripe Customer ID
|
||||
@@ -33,9 +33,7 @@ pub async fn moderator_user_lookup_by_stripe_customer_id_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorUserLookupByStripeCustomerIdResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let stripe_customer_id = request.stripe_customer_id.trim();
|
||||
|
||||
|
||||
+2
-4
@@ -17,7 +17,7 @@ use mysql_queries::queries::users::user::get::lookup_user_for_moderation::{
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const LEGACY_USER_TOKEN_PREFIX: &str = "U:";
|
||||
@@ -40,9 +40,7 @@ pub async fn moderator_user_lookup_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorUserLookupSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let search = request.search.trim();
|
||||
|
||||
|
||||
+2
-11
@@ -18,9 +18,7 @@ use mysql_queries::queries::users::user::update::set_user_ban_status::{
|
||||
use mysql_queries::queries::users::user_profiles::get_user_profile_by_username::get_user_profile_by_username;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// --- Request ---
|
||||
@@ -61,14 +59,7 @@ pub async fn moderation_ban_user_handler(
|
||||
) -> Result<Json<ModerationBanUserSuccessResponse>, CommonWebError> {
|
||||
|
||||
// 1. Require moderator with ban permissions.
|
||||
let user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
if !user_session.can_ban_users {
|
||||
warn!("User {} is not allowed to ban users", user_session.user_token.as_str());
|
||||
|
||||
+2
-11
@@ -24,9 +24,7 @@ use users::email::email_to_gravatar_hash::email_to_gravatar_hash;
|
||||
use users::email::validate_email_address_format::validate_email_address_format;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// --- Request ---
|
||||
@@ -71,14 +69,7 @@ pub async fn moderator_change_user_email_handler(
|
||||
) -> Result<Json<ModeratorChangeUserEmailSuccessResponse>, CommonWebError> {
|
||||
|
||||
// 1. Require moderator.
|
||||
let user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
// 2. Normalize + validate the new email.
|
||||
let new_email = request.new_email.trim().to_lowercase();
|
||||
|
||||
+2
-11
@@ -15,9 +15,7 @@ use mysql_queries::queries::users::user_email_changes::list_user_email_changes_f
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "modemchg";
|
||||
@@ -103,14 +101,7 @@ pub async fn moderator_list_email_address_changes_for_user_handler(
|
||||
) -> Result<Json<ModeratorListUserEmailChangesSuccessResponse>, CommonWebError> {
|
||||
|
||||
// 1. Require moderator.
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
// 2. Resolve username → user_token. Not-found → 404; everything else → 500.
|
||||
let username = path.username.trim().to_lowercase();
|
||||
|
||||
+2
-11
@@ -27,9 +27,7 @@ use crate::http_server::session::lookup::user_session_feature_flags::UserSession
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::web_utils::response_success_helpers::simple_json_success;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
use artcraft_api_defs::common::responses::simple_generic_json_success::SimpleGenericJsonSuccess;
|
||||
|
||||
@@ -89,14 +87,7 @@ pub async fn moderator_edit_user_feature_flags_handler(
|
||||
redis_pool: Data<r2d2::Pool<Client>>,
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let username_or_token = path.username_or_token.trim();
|
||||
|
||||
|
||||
+2
-11
@@ -8,9 +8,7 @@ use utoipa::ToSchema;
|
||||
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// ── Response ──
|
||||
@@ -49,14 +47,7 @@ pub async fn moderator_list_all_available_user_feature_flags_handler(
|
||||
http_request: HttpRequest,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorListUserFeatureFlagsResponse>, CommonWebError> {
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let feature_flags: Vec<FeatureFlagDescriptor> = UserFeatureFlag::all_variants()
|
||||
.into_iter()
|
||||
|
||||
+2
-11
@@ -12,9 +12,7 @@ use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::session::lookup::user_session_feature_flags::UserSessionFeatureFlags;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// ── Request ──
|
||||
@@ -56,14 +54,7 @@ pub async fn moderator_list_user_feature_flags_handler(
|
||||
path: Path<ListUserFeatureFlagsPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorListUserFeatureFlagsResponse>, CommonWebError> {
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let username_or_token = path.username_or_token.trim();
|
||||
|
||||
|
||||
+2
-11
@@ -13,9 +13,7 @@ use mysql_queries::queries::user_referrals::list_global_user_referrals::{
|
||||
};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "modusrref";
|
||||
@@ -42,14 +40,7 @@ pub async fn moderator_list_global_user_referrals_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListGlobalUserReferralsSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let limit = query.limit
|
||||
.unwrap_or(DEFAULT_LIMIT)
|
||||
|
||||
+2
-11
@@ -16,9 +16,7 @@ use mysql_queries::queries::user_referrals::list_user_referrals_for_user::{
|
||||
};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "modusrrefu";
|
||||
@@ -47,14 +45,7 @@ pub async fn moderator_list_user_referrals_for_user_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListUserReferralsForUserSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let limit = query.limit
|
||||
.unwrap_or(DEFAULT_LIMIT)
|
||||
|
||||
+2
-11
@@ -12,9 +12,7 @@ use mysql_queries::queries::user_impersonation_requests::list_user_impersonation
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "moduserimp";
|
||||
@@ -81,14 +79,7 @@ pub async fn moderator_list_user_session_impersonation_requests_for_user_handler
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListUserImpersonationRequestsSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let limit = query.limit
|
||||
.unwrap_or(DEFAULT_LIMIT)
|
||||
|
||||
+2
-11
@@ -12,9 +12,7 @@ use mysql_queries::queries::user_impersonation_requests::list_user_impersonation
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
const CURSOR_NAME: &str = "modallimp";
|
||||
@@ -75,14 +73,7 @@ pub async fn moderator_list_user_session_impersonation_requests_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListAllImpersonationRequestsSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let limit = query.limit
|
||||
.unwrap_or(DEFAULT_LIMIT)
|
||||
|
||||
+2
-11
@@ -25,9 +25,7 @@ use mysql_queries::queries::users::user::get::lookup_user_for_moderation::{
|
||||
};
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// --- Request ---
|
||||
@@ -71,14 +69,7 @@ pub async fn moderator_user_session_impersonation_request_handler(
|
||||
) -> Result<Json<ModerationImpersonateSuccessResponse>, CommonWebError> {
|
||||
|
||||
// 1. Require moderator session.
|
||||
let user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
if user_session.is_banned {
|
||||
warn!("Banned moderator tried to impersonate: {}", user_session.user_token.as_str());
|
||||
|
||||
+2
-8
@@ -16,7 +16,7 @@ use mysql_queries::queries::users::user_subscriptions::list_stripe_customer_ids_
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Get all stripe customer ids on file for a user (moderation)
|
||||
@@ -43,13 +43,7 @@ pub async fn moderator_get_user_stripe_customer_ids_handler(
|
||||
.acquire()
|
||||
.await?;
|
||||
|
||||
let _moderator_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::FromPool(&mut mysql_connection),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _moderator_session = require_moderator(&http_request, &server_state.session_checker, &mut *mysql_connection).await?;
|
||||
|
||||
let customer_links = list_user_stripe_customer_links_for_user(&path.user_token, &mut mysql_connection)
|
||||
.await
|
||||
|
||||
+2
-4
@@ -13,7 +13,7 @@ use mysql_queries::queries::wallet_ledger_entries::list_wallet_ledger_entries_by
|
||||
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// List wallet ledger entries by wallet token (moderation)
|
||||
@@ -36,9 +36,7 @@ pub async fn list_wallet_ledger_entries_by_wallet_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListWalletLedgerEntriesByWalletResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let results = list_wallet_ledger_entries_by_wallet(&path.wallet_token, &server_state.mysql_pool)
|
||||
.await
|
||||
|
||||
+2
-4
@@ -13,7 +13,7 @@ use mysql_queries::queries::wallet_ledger_entries::get_wallet_ledger_entry_for_m
|
||||
|
||||
use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Get a single wallet ledger entry by token (moderation)
|
||||
@@ -36,9 +36,7 @@ pub async fn moderator_get_wallet_ledger_entry_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorGetWalletLedgerEntryResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let maybe_entry = get_wallet_ledger_entry_for_moderation(&path.wallet_ledger_entry_token, &server_state.mysql_pool)
|
||||
.await
|
||||
|
||||
+2
-4
@@ -13,7 +13,7 @@ use mysql_queries::queries::wallets::list_user_wallets_for_moderation::list_user
|
||||
|
||||
use tokens::tokens::users::UserToken;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// List wallets for a user (moderation)
|
||||
@@ -36,9 +36,7 @@ pub async fn list_user_wallets_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListUserWalletsResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let results = list_user_wallets_for_moderation(&path.user_token, &server_state.mysql_pool)
|
||||
.await
|
||||
|
||||
+2
-7
@@ -21,7 +21,7 @@ use mysql_queries::queries::wallets::add_durable_banked_balance_to_wallet::add_d
|
||||
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Add banked credits to a wallet (moderation)
|
||||
@@ -46,12 +46,7 @@ pub async fn moderator_add_banked_balance_to_wallet_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorAddBankedBalanceToWalletResponse>, CommonWebError> {
|
||||
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
let user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
|
||||
+2
-4
@@ -15,7 +15,7 @@ use mysql_queries::queries::wallets::create_new_wallet_for_owner_user::create_ne
|
||||
use mysql_queries::queries::wallets::find_primary_wallet_token_for_owner::find_primary_wallet_token_for_owner_using_connection;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Create a wallet for a user (moderation)
|
||||
@@ -37,9 +37,7 @@ pub async fn moderator_create_wallet_for_user_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorCreateWalletForUserResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let user_token = request.user_token.as_ref()
|
||||
.ok_or_else(|| CommonWebError::BadInputWithSimpleMessage("user_token is required".to_string()))?;
|
||||
|
||||
+2
-4
@@ -13,7 +13,7 @@ use mysql_queries::queries::wallets::get_wallet_for_moderation::get_wallet_for_m
|
||||
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::http_server::web_utils::user_session::require_moderator::require_moderator;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Get a single wallet by token (moderation)
|
||||
@@ -36,9 +36,7 @@ pub async fn moderator_get_wallet_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorGetWalletResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
let _user_session = require_moderator(&http_request, &server_state.session_checker, &server_state.mysql_pool).await?;
|
||||
|
||||
let maybe_wallet = get_wallet_for_moderation(&path.wallet_token, &server_state.mysql_pool)
|
||||
.await
|
||||
|
||||
+7
-1
@@ -143,14 +143,20 @@ pub async fn omni_gen_image_generate_handler(
|
||||
|
||||
// ==================== PIPELINE ==================== //
|
||||
|
||||
// NB: Release the pooled DB connection before the (slow, external) generation call so we don't
|
||||
// hold a pool slot idle while waiting on the provider — that's what starves the pool and causes
|
||||
// PoolTimedOut on unrelated endpoints. We re-acquire below to write the result.
|
||||
drop(mysql_connection);
|
||||
|
||||
let pipeline_result = run_pipeline_v2(RunPipelineV2Args {
|
||||
router_builder: &router_builder,
|
||||
server_state: &server_state,
|
||||
mysql_connection: &mut mysql_connection,
|
||||
user_token,
|
||||
resolved_media: &resolved_media,
|
||||
}).await?;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
// ==================== DEBUG LOG: FAL REQUEST ==================== //
|
||||
|
||||
if let GenerateImageResponse::Fal(ref fal_payload) = pipeline_result.response {
|
||||
|
||||
+12
-3
@@ -25,18 +25,20 @@ use crate::util::lookup::lookup_media_files_as_cdn_url_list_and_map::MediaFilesA
|
||||
pub struct RunPipelineV2Args<'a> {
|
||||
pub router_builder: &'a GenerateImageRequestBuilder,
|
||||
pub server_state: &'a ServerState,
|
||||
pub mysql_connection: &'a mut sqlx::pool::PoolConnection<sqlx::MySql>,
|
||||
pub user_token: &'a UserToken,
|
||||
pub resolved_media: &'a MediaFilesAsCdnUrlListAndMap,
|
||||
}
|
||||
|
||||
// NB: This pipeline does an external generation call (`finalize_and_generate`) that can take many
|
||||
// seconds. It deliberately does NOT hold a pooled DB connection across that call — it acquires a
|
||||
// short-lived connection only for the wallet deduction. Holding a pooled connection across the
|
||||
// external call is what starves the pool and causes `PoolTimedOut`.
|
||||
pub async fn run_pipeline_v2(
|
||||
args: RunPipelineV2Args<'_>,
|
||||
) -> Result<ImagePipelineResult, CommonWebError> {
|
||||
let RunPipelineV2Args {
|
||||
router_builder,
|
||||
server_state,
|
||||
mysql_connection,
|
||||
user_token,
|
||||
resolved_media,
|
||||
} = args;
|
||||
@@ -55,17 +57,24 @@ pub async fn run_pipeline_v2(
|
||||
let apriori_job_token = InferenceJobToken::generate();
|
||||
|
||||
let maybe_wallet_ledger_entry_token = if cost > 0 {
|
||||
// NB: Short-lived connection — released before the external call below.
|
||||
let mut billing_connection = server_state.mysql_pool.acquire().await
|
||||
.map_err(|err| {
|
||||
warn!("Failed to acquire MySQL connection for image billing: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
let deduction = attempt_wallet_deduction_else_common_web_error(
|
||||
user_token,
|
||||
Some(apriori_job_token.as_str()),
|
||||
cost,
|
||||
mysql_connection,
|
||||
&mut billing_connection,
|
||||
).await?;
|
||||
Some(deduction.ledger_entry_token)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// NB: No pooled DB connection is held across this external generation call.
|
||||
let response = finalize_and_generate(draft_or_request, server_state, resolved_media).await?;
|
||||
|
||||
Ok(ImagePipelineResult {
|
||||
|
||||
+7
-1
@@ -200,16 +200,22 @@ pub async fn omni_gen_video_generate_handler(
|
||||
warn!("Failed to insert HTTP request debug log: {:?}", err);
|
||||
}
|
||||
|
||||
// NB: Release the pooled DB connection before the (slow, external) generation call so we don't
|
||||
// hold a pool slot idle while waiting on the provider — that's what starves the pool and causes
|
||||
// PoolTimedOut on unrelated endpoints. We re-acquire below to write the result.
|
||||
drop(mysql_connection);
|
||||
|
||||
let pipeline_result = run_pipeline_v2(RunPipelineV2Args {
|
||||
router_builder: &router_builder,
|
||||
server_state: &server_state,
|
||||
mysql_connection: &mut mysql_connection,
|
||||
user_token,
|
||||
media_file_to_url_map: &media_file_to_url_map,
|
||||
kinovi_character_id_map: &kinovi_character_id_map,
|
||||
kinovi_account,
|
||||
}).await?;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
// ==================== DEBUG LOG: FAL REQUEST ==================== //
|
||||
|
||||
if let GenerateVideoResponse::Fal(ref fal_payload) = pipeline_result.response {
|
||||
|
||||
+23
-9
@@ -3,7 +3,6 @@ use std::convert::TryFrom;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use log::{error, info, warn};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use artcraft_router::api::router_video_model::RouterVideoModel;
|
||||
use artcraft_router::api::router_provider::RouterProvider;
|
||||
use artcraft_router::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
@@ -28,18 +27,20 @@ use crate::state::server_state::ServerState;
|
||||
pub struct RunPipelineV2Args<'a> {
|
||||
pub router_builder: &'a GenerateVideoRequestBuilder,
|
||||
pub server_state: &'a ServerState,
|
||||
pub mysql_connection: &'a mut PoolConnection<sqlx::MySql>,
|
||||
pub user_token: &'a UserToken,
|
||||
pub media_file_to_url_map: &'a Option<HashMap<MediaFileToken, String>>,
|
||||
pub kinovi_character_id_map: &'a Option<HashMap<CharacterToken, String>>,
|
||||
pub kinovi_account: KinoviAccount,
|
||||
}
|
||||
|
||||
// NB: This pipeline does an external generation call (`upload_and_generate`) that can take many
|
||||
// seconds. It deliberately does NOT hold a pooled DB connection across that call — it acquires
|
||||
// short-lived connections only for the billing and (on failure) refund writes. Holding a pooled
|
||||
// connection across the external call is what starves the pool and causes `PoolTimedOut`.
|
||||
pub async fn run_pipeline_v2(args: RunPipelineV2Args<'_>) -> Result<PipelineResult, CommonWebError> {
|
||||
let RunPipelineV2Args {
|
||||
router_builder,
|
||||
server_state,
|
||||
mysql_connection,
|
||||
user_token,
|
||||
media_file_to_url_map,
|
||||
kinovi_character_id_map,
|
||||
@@ -137,11 +138,19 @@ pub async fn run_pipeline_v2(args: RunPipelineV2Args<'_>) -> Result<PipelineResu
|
||||
|
||||
info!("v2 estimated cost: {} credits (estimates: {:?})", cost, cost_estimates);
|
||||
|
||||
// 3. Bill wallet
|
||||
let billing = bill_wallet(user_token, cost, mysql_connection).await?;
|
||||
// 3. Bill wallet (short-lived connection — released before the external call below).
|
||||
let billing = {
|
||||
let mut billing_connection = server_state.mysql_pool.acquire().await
|
||||
.map_err(|err| {
|
||||
error!("Failed to acquire MySQL connection for billing: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
bill_wallet(user_token, cost, &mut billing_connection).await?
|
||||
};
|
||||
|
||||
// 4. Upload media (if draft) and generate video.
|
||||
// The entire block is wrapped so Kinovi failures trigger a refund.
|
||||
// NB: No pooled DB connection is held across this call.
|
||||
let result = upload_and_generate(
|
||||
draft_or_request,
|
||||
server_state,
|
||||
@@ -156,10 +165,15 @@ pub async fn run_pipeline_v2(args: RunPipelineV2Args<'_>) -> Result<PipelineResu
|
||||
if let Some(ledger_entry_token) = billing.maybe_wallet_ledger_entry_token.as_ref() {
|
||||
warn!("Kinovi v2 generation failed, issuing refund for {}: {:?}", ledger_entry_token.as_str(), err);
|
||||
|
||||
let result = refund_wallet_after_api_failure(ledger_entry_token, mysql_connection).await;
|
||||
|
||||
if let Err(refund_err) = result {
|
||||
error!("Failed to refund wallet after Kinovi v2 failure: {:?}", refund_err);
|
||||
match server_state.mysql_pool.acquire().await {
|
||||
Ok(mut refund_connection) => {
|
||||
if let Err(refund_err) = refund_wallet_after_api_failure(ledger_entry_token, &mut refund_connection).await {
|
||||
error!("Failed to refund wallet after Kinovi v2 failure: {:?}", refund_err);
|
||||
}
|
||||
}
|
||||
Err(acquire_err) => {
|
||||
error!("Failed to acquire MySQL connection to refund wallet after Kinovi v2 failure: {:?}", acquire_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user