mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 18:21:12 +00:00
Merge branch 'master' into feature/weights_endpoints
This commit is contained in:
commit
024b4d769c
59 files changed
+1508
-180
No files matched your search
Generated
+222
-48
@@ -468,6 +468,12 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@@ -521,7 +527,7 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -531,7 +537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1037,18 +1043,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.22"
|
||||
version = "0.4.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time 0.1.45",
|
||||
"wasm-bindgen",
|
||||
"winapi 0.3.9",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2196,12 +2201,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2220,7 +2225,7 @@ checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"home",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2714,7 +2719,7 @@ version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2907,16 +2912,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
@@ -2964,6 +2959,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3079,7 +3075,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.3",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3215,9 +3211,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a466bc111374ccf4d90877dba636924a2185e67e5be4b35d32043199365097b2"
|
||||
checksum = "a48c2e9831b370bc2d7233c2620298c45f3a158ed6b4b8d7416b2ada5a268fd8"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"chumsky",
|
||||
@@ -3227,7 +3223,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hostname",
|
||||
"httpdate",
|
||||
"idna 0.4.0",
|
||||
"idna",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"nom",
|
||||
@@ -3600,7 +3596,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3736,7 +3732,7 @@ dependencies = [
|
||||
"url",
|
||||
"void",
|
||||
"winapi 0.3.9",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4131,7 +4127,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall 0.4.1",
|
||||
"smallvec 1.11.2",
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4393,6 +4389,30 @@ dependencies = [
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
@@ -4841,7 +4861,7 @@ dependencies = [
|
||||
"libc",
|
||||
"spin 0.9.8",
|
||||
"untrusted 0.9.0",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4876,6 +4896,41 @@ dependencies = [
|
||||
"quick-xml 0.30.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3d8c6fd84090ae348e63a84336b112b5c3918b3bf0493a581f7bd8ee623c29"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"shellexpand",
|
||||
"syn 2.0.39",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "873feff8cb7bf86fdf0a71bb21c95159f4e4a37dd7a4bd1855a940909b583ada"
|
||||
dependencies = [
|
||||
"sha2 0.10.8",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.18.0"
|
||||
@@ -4951,15 +5006,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.25"
|
||||
version = "0.38.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
|
||||
checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5061,7 +5116,7 @@ version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5411,6 +5466,15 @@ dependencies = [
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
@@ -5514,7 +5578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5894,6 +5958,8 @@ dependencies = [
|
||||
"url_config",
|
||||
"user_input_common",
|
||||
"users_component",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"uuid 0.8.2",
|
||||
]
|
||||
|
||||
@@ -6231,7 +6297,7 @@ dependencies = [
|
||||
"fastrand 2.0.1",
|
||||
"redox_syscall 0.4.1",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6387,6 +6453,7 @@ dependencies = [
|
||||
"sqlx-core",
|
||||
"strum",
|
||||
"toml",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6405,7 +6472,7 @@ dependencies = [
|
||||
"signal-hook-registry",
|
||||
"socket2 0.5.5",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6935,7 +7002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna 0.5.0",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
@@ -7025,6 +7092,47 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ff05e3bac2c9428f57ade702667753ca3f5cf085e2011fe697de5bfd49aa72d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0b6f4667edd64be0e820d6631a60433a269710b6ee89ac39525b872b76d61d"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-swagger-ui"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f839caa8e09dddc3ff1c3112a91ef7da0601075ba5025d9f33ae99c4cb9b6e51"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"mime_guess",
|
||||
"regex",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
@@ -7419,7 +7527,7 @@ version = "0.51.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7428,7 +7536,16 @@ version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7437,13 +7554,28 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7452,42 +7584,84 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
@@ -7495,7 +7669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -29,7 +29,9 @@ Web API Overview
|
||||
* `tts-inference-job` - (*deprecated*) Async TTS inference of Tacotron2 models only
|
||||
* `w2l-download-job` - (*deprecated*) Async download of videos and images
|
||||
* `w2l-inference-job` - (*deprecated*) Async W2L inference
|
||||
|
||||
* Documentation Server:
|
||||
* `docs` - this will run a local swagger server with the endpoints that you have access to, http://127.0.0.1:8989/api-docs/openapi.json use this to port everything over to the post man.
|
||||
|
||||
### PowerStream
|
||||
|
||||
* [PowerStream](https://power.stream) (deprecated) is powered by the following applications:
|
||||
|
||||
@@ -380,3 +380,16 @@ of this error message. You can essentially ignore it.
|
||||
### [Fix] Can't connect to local MySQL after install on Ubuntu
|
||||
|
||||
- If MySql in local dev can't be connected to, [reset the accounts](https://linuxconfig.org/how-to-reset-root-mysql-mariadb-password-on-ubuntu-20-04-focal-fossa-linux).
|
||||
|
||||
### API Server
|
||||
```
|
||||
cargo run --bin docs
|
||||
```
|
||||
This setups an api server with swagger docs to show you responses and requests with each associated endpoint
|
||||
It is run at http://127.0.0.1:8989/
|
||||
To obtain the json for the postman collection go to this url http://127.0.0.1:8989/api-docs/openapi.json
|
||||
We use this: `https://github.com/juhaku/utoipa`
|
||||
The documentation example is in:
|
||||
`crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/inference/enqueue_tts_request.rs`
|
||||
Write into the api doc to:
|
||||
`crates/service/web/storyteller_web/src/api_doc.rs`
|
||||
@@ -17,10 +17,6 @@ path = "src/lib.rs"
|
||||
serde = "1.0.125"
|
||||
serde_derive = "1.0.125"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "sqlite", "runtime-tokio-native-tls", "chrono" ] }
|
||||
sqlx-core = { version = "0.7.2" }
|
||||
sqlx-mysql= { version = "0.7.2" }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
#[cfg(test)]
|
||||
@@ -29,6 +30,7 @@ pub enum MediaFileOriginCategory {
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(MediaFileOriginCategory);
|
||||
impl_mysql_enum_coders!(MediaFileOriginCategory);
|
||||
impl_mysql_from_row!(MediaFileOriginCategory);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl MediaFileOriginCategory {
|
||||
|
||||
@@ -24,6 +24,7 @@ pub enum MediaFileOriginModelType {
|
||||
/// so-vits-svc voice conversion models
|
||||
#[serde(rename = "so_vits_svc")]
|
||||
SoVitsSvc,
|
||||
|
||||
#[serde(rename = "vall_e_x")]
|
||||
VallEX,
|
||||
}
|
||||
@@ -31,6 +32,7 @@ pub enum MediaFileOriginModelType {
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(MediaFileOriginModelType);
|
||||
impl_mysql_enum_coders!(MediaFileOriginModelType);
|
||||
impl_mysql_from_row!(MediaFileOriginModelType);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl MediaFileOriginModelType {
|
||||
|
||||
@@ -39,6 +39,7 @@ pub enum MediaFileOriginProductCategory {
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(MediaFileOriginProductCategory);
|
||||
impl_mysql_enum_coders!(MediaFileOriginProductCategory);
|
||||
impl_mysql_from_row!(MediaFileOriginProductCategory);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl MediaFileOriginProductCategory {
|
||||
|
||||
@@ -25,6 +25,7 @@ pub enum MediaFileType {
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(MediaFileType);
|
||||
impl_mysql_enum_coders!(MediaFileType);
|
||||
impl_mysql_from_row!(MediaFileType);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl MediaFileType {
|
||||
|
||||
@@ -38,6 +38,7 @@ pub enum Visibility {
|
||||
|
||||
|
||||
impl_enum_display_and_debug_using_to_str!(Visibility);
|
||||
impl_mysql_from_row!(Visibility);
|
||||
|
||||
// For reference, here's what the serde implementation might be if manually written.
|
||||
// This may be useful for designing composite types in the future:
|
||||
|
||||
@@ -32,3 +32,4 @@ mod macros;
|
||||
|
||||
pub mod by_table;
|
||||
pub mod common;
|
||||
pub mod traits;
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
/// Implement `MySqlFromRow` on a type.
|
||||
macro_rules! impl_mysql_from_row {
|
||||
($t:ident) => {
|
||||
|
||||
// Try to convert a MySQL row and named field into the value type (for non-nullable fields).
|
||||
impl crate::traits::mysql_from_row::MySqlFromRow<$t> for $t {
|
||||
fn try_from_mysql_row(row: &sqlx_mysql::MySqlRow, field_name: &str) -> Result<$t, sqlx::Error> {
|
||||
use sqlx::{Error, Row};
|
||||
|
||||
// NB(bt,2023-12-05): For now only string encodings are considered.
|
||||
// We may want to revisit in the future if we deal with binary data.
|
||||
let value : String = row.try_get(field_name)?;
|
||||
|
||||
let output_type = Self::from_str(&value)
|
||||
.map_err(|err| {
|
||||
Error::ColumnDecode {
|
||||
index: format!("mysql_from_row failure on field {}: {:?}", field_name, err),
|
||||
source: format!("mysql_from_row failure on field {}: {:?}", field_name, err).into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(output_type)
|
||||
}
|
||||
|
||||
// Try to convert a MySQL row and named field into the value type (for nullable fields).
|
||||
fn try_from_mysql_row_nullable(row: &sqlx_mysql::MySqlRow, field_name: &str) -> Result<Option<$t>, sqlx::Error> {
|
||||
use sqlx::{Error, Row};
|
||||
|
||||
// NB(bt,2023-12-05): For now only string encodings are considered.
|
||||
// We may want to revisit in the future if we deal with binary data.
|
||||
// NB(2): Nullable fields decode as Option<T>.
|
||||
let maybe_value : Option<String> = row.try_get(field_name)?;
|
||||
|
||||
let value = match maybe_value {
|
||||
Some(value) => value,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let output_type = Self::from_str(&value)
|
||||
.map_err(|err| {
|
||||
Error::ColumnDecode {
|
||||
index: format!("mysql_from_row failure on field {}: {:?}", field_name, err),
|
||||
source: format!("mysql_from_row failure on field {}: {:?}", field_name, err).into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Some(output_type))
|
||||
}
|
||||
}
|
||||
|
||||
//// TODO: Tests.
|
||||
//#[cfg(test)]
|
||||
//#[test]
|
||||
//fn test_name() {
|
||||
// use strum::IntoEnumIterator;
|
||||
// for variant in $t::iter() {
|
||||
// assert_eq!(format!("{}", variant), variant.to_str());
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#[macro_use] pub mod impl_enum_display_and_debug_using_to_str;
|
||||
#[macro_use] pub mod impl_mysql_enum_coders;
|
||||
#[macro_use] pub mod impl_mysql_from_row;
|
||||
#[macro_use] pub mod impl_sqlite_enum_coders;
|
||||
@@ -0,0 +1 @@
|
||||
pub mod mysql_from_row;
|
||||
@@ -0,0 +1,11 @@
|
||||
use sqlx_mysql::MySqlRow;
|
||||
|
||||
/// This is a trait (in conjunction with the macro implementation) that makes up for sqlx's
|
||||
/// QueryBuilder not playing well with FromRow implementation or derivation of named types.
|
||||
pub trait MySqlFromRow<T> {
|
||||
|
||||
/// Attempt to parse a named column from a MySqlRow into the type.
|
||||
fn try_from_mysql_row(row: &MySqlRow, field_name: &str) -> Result<T, sqlx::Error>;
|
||||
|
||||
fn try_from_mysql_row_nullable(row: &MySqlRow, field_name: &str) -> Result<Option<T>, sqlx::Error>;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ path = "src/lib.rs"
|
||||
# (none)
|
||||
|
||||
# External
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
|
||||
# Serialization
|
||||
serde = "1.0.125"
|
||||
|
||||
@@ -20,14 +20,12 @@ serde_derive = "1.0.125"
|
||||
# Random
|
||||
rand = "0.8.5"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
sqlx-core = { version = "0.7.2" }
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
|
||||
utoipa = { version = "4", features = ["actix_extras"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
# Enum utilities
|
||||
|
||||
@@ -4,9 +4,10 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::prefixes::TokenPrefix;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// The primary key for "generic" inference jobs.
|
||||
#[derive(Clone, PartialEq, Eq, sqlx::Type, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, sqlx::Type, Debug, Serialize, Deserialize,ToSchema)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct InferenceJobToken(String);
|
||||
|
||||
|
||||
@@ -36,10 +36,6 @@ clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updat
|
||||
# NB: We switched to Tokio from `async-std` for broader ecosystem support.
|
||||
tokio = { version = "1.6.0", features = ["macros"] }
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
elasticsearch = "8.5.0-alpha.1"
|
||||
|
||||
@@ -33,10 +33,6 @@ log = "0.4.14"
|
||||
# NB: We switched to Tokio from `async-std` for broader ecosystem support.
|
||||
tokio = { version = "1.6.0", features = ["macros"] }
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
|
||||
|
||||
@@ -33,10 +33,6 @@ strum = { version = "0.25", features = ["derive"] }
|
||||
# NB: We switched to Tokio from `async-std` for broader ecosystem support.
|
||||
tokio = { version = "1.6.0", features = ["macros"] }
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
elasticsearch = "8.5.0-alpha.1"
|
||||
|
||||
@@ -13,7 +13,7 @@ name = "datetimes"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
once_cell = "1.18.0"
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ reusable_types = { path = "../../../_types/reusable_types" }
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.0"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
log = "0.4.14"
|
||||
once_cell = "1.18.0"
|
||||
regex = "1.5.4"
|
||||
|
||||
@@ -23,7 +23,7 @@ elasticsearch = "8.5.0-alpha.1"
|
||||
log = "0.4.14"
|
||||
|
||||
# Serialization
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.108"
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
|
||||
@@ -11,5 +11,5 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
errors = { path = "../errors" }
|
||||
lettre = "0.11.0"
|
||||
lettre = "0.11.2"
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
@@ -24,7 +24,7 @@ redis_common = { path = "../redis_common" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
log = "0.4.14"
|
||||
r2d2_redis = "0.14.0"
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
@@ -25,7 +25,7 @@ tokens = { path = "../../_types/tokens" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
log = "0.4.14"
|
||||
|
||||
@@ -39,10 +39,6 @@ serde_derive = "1.0.125"
|
||||
serde_json = "1.0.108"
|
||||
toml = "0.5.8"
|
||||
serial_test = "2.0.0"
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
|
||||
tokio = { version = "1.6.0", features = ["full"] }
|
||||
rand = "0.8"
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::{FromRow, MySql, MySqlPool, QueryBuilder, Row};
|
||||
use sqlx::mysql::MySqlRow;
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use enums::traits::mysql_from_row::MySqlFromRow;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub struct MediaFileListPage {
|
||||
pub records: Vec<MediaFileListItem>,
|
||||
|
||||
pub sort_ascending: bool,
|
||||
|
||||
/// ID of the first record.
|
||||
pub first_id: Option<i64>,
|
||||
|
||||
/// ID of the last record.
|
||||
pub last_id: Option<i64>,
|
||||
}
|
||||
|
||||
pub struct MediaFileListItem {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub origin_category: MediaFileOriginCategory,
|
||||
pub origin_product_category: MediaFileOriginProductCategory,
|
||||
pub maybe_origin_model_type: Option<MediaFileOriginModelType>,
|
||||
pub maybe_origin_model_token: Option<String>,
|
||||
|
||||
pub media_type: MediaFileType,
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub maybe_creator_username: Option<String>,
|
||||
pub maybe_creator_display_name: Option<String>,
|
||||
pub maybe_creator_gravatar_hash: Option<String>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ViewAs {
|
||||
Moderator,
|
||||
NonModerator,
|
||||
}
|
||||
|
||||
pub struct ListMediaFilesArgs<'a> {
|
||||
pub limit: usize,
|
||||
pub maybe_filter_media_type: Option<MediaFileType>,
|
||||
pub maybe_offset: Option<usize>,
|
||||
pub cursor_is_reversed: bool,
|
||||
pub view_as: ViewAs,
|
||||
pub mysql_pool: &'a MySqlPool,
|
||||
}
|
||||
|
||||
pub async fn list_media_files(args: ListMediaFilesArgs<'_>) -> AnyhowResult<MediaFileListPage> {
|
||||
|
||||
let mut query = query_builder(
|
||||
args.maybe_filter_media_type,
|
||||
args.limit,
|
||||
args.maybe_offset,
|
||||
args.cursor_is_reversed,
|
||||
args.view_as,
|
||||
);
|
||||
|
||||
let query = query.build_query_as::<MediaFileListItemInternal>();
|
||||
|
||||
let results = query.fetch_all(args.mysql_pool).await?;
|
||||
|
||||
let first_id = results.first()
|
||||
.map(|raw_result| raw_result.id);
|
||||
|
||||
let last_id = results.last()
|
||||
.map(|raw_result| raw_result.id);
|
||||
|
||||
let results = results.into_iter()
|
||||
.map(|record| {
|
||||
MediaFileListItem {
|
||||
token: record.token,
|
||||
origin_category: record.origin_category,
|
||||
origin_product_category: record.origin_product_category,
|
||||
maybe_origin_model_type: record.maybe_origin_model_type,
|
||||
maybe_origin_model_token: record.maybe_origin_model_token,
|
||||
media_type: record.media_type,
|
||||
public_bucket_directory_hash: record.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: record.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: record.maybe_public_bucket_extension,
|
||||
maybe_creator_user_token: record.maybe_creator_user_token,
|
||||
maybe_creator_username: record.maybe_creator_username,
|
||||
maybe_creator_display_name: record.maybe_creator_display_name,
|
||||
maybe_creator_gravatar_hash: record.maybe_creator_gravatar_hash,
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(MediaFileListPage {
|
||||
records: results,
|
||||
sort_ascending: !args.cursor_is_reversed,
|
||||
first_id,
|
||||
last_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn query_builder<'a>(
|
||||
maybe_filter_media_type: Option<MediaFileType>,
|
||||
limit: usize,
|
||||
maybe_offset: Option<usize>,
|
||||
cursor_is_reversed: bool,
|
||||
view_as: ViewAs,
|
||||
) -> QueryBuilder<'a, MySql> {
|
||||
|
||||
// NB: Query cannot be statically checked by sqlx
|
||||
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
|
||||
r#"
|
||||
SELECT
|
||||
m.id,
|
||||
m.token,
|
||||
|
||||
m.origin_category,
|
||||
m.origin_product_category,
|
||||
|
||||
m.maybe_origin_model_type,
|
||||
m.maybe_origin_model_token,
|
||||
|
||||
m.media_type,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
m.maybe_creator_user_token,
|
||||
u.username as maybe_creator_username,
|
||||
u.display_name as maybe_creator_display_name,
|
||||
u.email_gravatar_hash as maybe_creator_gravatar_hash,
|
||||
|
||||
m.creator_set_visibility,
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m
|
||||
LEFT OUTER JOIN users AS u
|
||||
ON m.maybe_creator_user_token = u.token
|
||||
|
||||
WHERE m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
"#
|
||||
);
|
||||
|
||||
if let Some(media_type) = maybe_filter_media_type {
|
||||
// FIXME: Binding shouldn't require to_str().
|
||||
// Otherwise, it's calling the Display trait on the raw type which is resulting in an
|
||||
// incorrect binding and runtime error.
|
||||
query_builder.push(" AND m.media_file_type = ");
|
||||
query_builder.push_bind(media_type.to_str());
|
||||
}
|
||||
|
||||
match view_as {
|
||||
ViewAs::Moderator => {}
|
||||
ViewAs::NonModerator=> {
|
||||
// FIXME: Binding shouldn't require to_str().
|
||||
// Otherwise, it's calling the Display trait on the raw type which is resulting in an
|
||||
// incorrect binding and runtime error.
|
||||
query_builder.push(" AND m.creator_set_visibility = ");
|
||||
query_builder.push_bind(Visibility::Public.to_str());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(offset) = maybe_offset {
|
||||
if cursor_is_reversed {
|
||||
query_builder.push(format!(" AND m.id < {offset} "));
|
||||
} else {
|
||||
query_builder.push(format!(" AND m.id > {offset} "));
|
||||
}
|
||||
}
|
||||
|
||||
if cursor_is_reversed {
|
||||
query_builder.push(" ORDER BY m.id ASC ");
|
||||
} else {
|
||||
query_builder.push(" ORDER BY m.id DESC ");
|
||||
}
|
||||
|
||||
query_builder.push(format!(" LIMIT {limit} "));
|
||||
|
||||
query_builder
|
||||
}
|
||||
|
||||
struct MediaFileListItemInternal {
|
||||
id: i64,
|
||||
token: MediaFileToken,
|
||||
|
||||
origin_category: MediaFileOriginCategory,
|
||||
origin_product_category: MediaFileOriginProductCategory,
|
||||
maybe_origin_model_type: Option<MediaFileOriginModelType>,
|
||||
maybe_origin_model_token: Option<String>,
|
||||
|
||||
media_type: MediaFileType,
|
||||
public_bucket_directory_hash: String,
|
||||
maybe_public_bucket_prefix: Option<String>,
|
||||
maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
maybe_creator_user_token: Option<UserToken>,
|
||||
maybe_creator_username: Option<String>,
|
||||
maybe_creator_display_name: Option<String>,
|
||||
maybe_creator_gravatar_hash: Option<String>,
|
||||
|
||||
creator_set_visibility: Visibility,
|
||||
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
// NB(bt,2023-12-05): There's an issue with type hinting in the `as` clauses with QueryBuilder (or
|
||||
// raw query strings) and sqlx::FromRow, regardless of whether it is derived of manually
|
||||
// implemented. Perhaps this will improve in the future, but for now manually constructed queries
|
||||
// cannot have type hints, eg. the following:
|
||||
//
|
||||
// m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
// m.origin_category as `origin_category: enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory`,
|
||||
// m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
//
|
||||
// This results in the automatic mapping not being able to be found by name (for macro derive), and
|
||||
// in the manual case `row.try_get()` etc. won't have the correct column name (since the name is the
|
||||
// full "as" clause).
|
||||
impl FromRow<'_, MySqlRow> for MediaFileListItemInternal {
|
||||
fn from_row(row: &MySqlRow) -> Result<Self, sqlx::Error> {
|
||||
let maybe_creator_user_token : Option<String> = row.try_get("maybe_creator_user_token")?;
|
||||
let maybe_creator_user_token = maybe_creator_user_token.map(|user_token| UserToken::new(user_token));
|
||||
|
||||
Ok(Self {
|
||||
id: row.try_get("id")?,
|
||||
token: MediaFileToken::new(row.try_get("token")?),
|
||||
origin_category: MediaFileOriginCategory::try_from_mysql_row(row, "origin_category")?,
|
||||
origin_product_category: MediaFileOriginProductCategory::try_from_mysql_row(row, "origin_product_category")?,
|
||||
maybe_origin_model_type: MediaFileOriginModelType::try_from_mysql_row_nullable(row, "maybe_origin_model_type")?,
|
||||
maybe_origin_model_token: row.try_get("maybe_origin_model_token")?,
|
||||
media_type: MediaFileType::try_from_mysql_row(row, "media_type")?,
|
||||
public_bucket_directory_hash: row.try_get("public_bucket_directory_hash")?,
|
||||
maybe_public_bucket_prefix: row.try_get("maybe_public_bucket_prefix")?,
|
||||
maybe_public_bucket_extension: row.try_get("maybe_public_bucket_extension")?,
|
||||
maybe_creator_user_token,
|
||||
maybe_creator_username: row.try_get("maybe_creator_username")?,
|
||||
maybe_creator_display_name: row.try_get("maybe_creator_display_name")?,
|
||||
maybe_creator_gravatar_hash: row.try_get("maybe_creator_gravatar_hash")?,
|
||||
creator_set_visibility: Visibility::try_from_mysql_row(row, "creator_set_visibility")?,
|
||||
created_at: row.try_get("created_at")?,
|
||||
updated_at: row.try_get("updated_at")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::{FromRow, MySql, MySqlPool, QueryBuilder, Row};
|
||||
use sqlx::mysql::MySqlRow;
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use enums::traits::mysql_from_row::MySqlFromRow;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub struct MediaFileListPage {
|
||||
pub records: Vec<MediaFileListItem>,
|
||||
|
||||
pub sort_ascending: bool,
|
||||
|
||||
/// ID of the first record.
|
||||
pub first_id: Option<i64>,
|
||||
|
||||
/// ID of the last record.
|
||||
pub last_id: Option<i64>,
|
||||
}
|
||||
|
||||
pub struct MediaFileListItem {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub origin_category: MediaFileOriginCategory,
|
||||
pub origin_product_category: MediaFileOriginProductCategory,
|
||||
pub maybe_origin_model_type: Option<MediaFileOriginModelType>,
|
||||
pub maybe_origin_model_token: Option<String>,
|
||||
|
||||
pub media_type: MediaFileType,
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ViewAs {
|
||||
Author,
|
||||
Moderator,
|
||||
AnotherUser,
|
||||
}
|
||||
|
||||
pub struct ListMediaFileForUserArgs<'a> {
|
||||
pub username: &'a str,
|
||||
pub limit: usize,
|
||||
pub maybe_filter_media_type: Option<MediaFileType>,
|
||||
pub maybe_offset: Option<usize>,
|
||||
pub cursor_is_reversed: bool,
|
||||
pub view_as: ViewAs,
|
||||
pub mysql_pool: &'a MySqlPool,
|
||||
}
|
||||
|
||||
pub async fn list_media_files_for_user(args: ListMediaFileForUserArgs<'_>) -> AnyhowResult<MediaFileListPage> {
|
||||
|
||||
let mut query = query_builder(
|
||||
args.maybe_filter_media_type,
|
||||
args.username,
|
||||
args.limit,
|
||||
args.maybe_offset,
|
||||
args.cursor_is_reversed,
|
||||
args.view_as,
|
||||
);
|
||||
|
||||
let query = query.build_query_as::<MediaFileListItemInternal>();
|
||||
|
||||
let results = query.fetch_all(args.mysql_pool).await?;
|
||||
|
||||
let first_id = results.first()
|
||||
.map(|raw_result| raw_result.id);
|
||||
|
||||
let last_id = results.last()
|
||||
.map(|raw_result| raw_result.id);
|
||||
|
||||
let results = results.into_iter()
|
||||
.map(|record| {
|
||||
MediaFileListItem {
|
||||
token: record.token,
|
||||
origin_category: record.origin_category,
|
||||
origin_product_category: record.origin_product_category,
|
||||
maybe_origin_model_type: record.maybe_origin_model_type,
|
||||
maybe_origin_model_token: record.maybe_origin_model_token,
|
||||
media_type: record.media_type,
|
||||
public_bucket_directory_hash: record.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: record.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: record.maybe_public_bucket_extension,
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(MediaFileListPage {
|
||||
records: results,
|
||||
sort_ascending: !args.cursor_is_reversed,
|
||||
first_id,
|
||||
last_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn query_builder<'a>(
|
||||
maybe_filter_media_type: Option<MediaFileType>,
|
||||
username: &'a str,
|
||||
limit: usize,
|
||||
maybe_offset: Option<usize>,
|
||||
cursor_is_reversed: bool,
|
||||
view_as: ViewAs,
|
||||
) -> QueryBuilder<'a, MySql> {
|
||||
|
||||
// NB: Query cannot be statically checked by sqlx
|
||||
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
|
||||
r#"
|
||||
SELECT
|
||||
m.id,
|
||||
m.token,
|
||||
|
||||
m.origin_category,
|
||||
m.origin_product_category,
|
||||
|
||||
m.maybe_origin_model_type,
|
||||
m.maybe_origin_model_token,
|
||||
|
||||
m.media_type,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
m.creator_set_visibility,
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m
|
||||
LEFT OUTER JOIN users AS u
|
||||
ON m.maybe_creator_user_token = u.token
|
||||
|
||||
WHERE m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
"#
|
||||
);
|
||||
|
||||
query_builder.push(" AND u.username = ");
|
||||
query_builder.push_bind(username);
|
||||
|
||||
if let Some(media_type) = maybe_filter_media_type {
|
||||
// FIXME: Binding shouldn't require to_str().
|
||||
// Otherwise, it's calling the Display trait on the raw type which is resulting in an
|
||||
// incorrect binding and runtime error.
|
||||
query_builder.push(" AND m.media_file_type = ");
|
||||
query_builder.push_bind(media_type.to_str());
|
||||
}
|
||||
|
||||
match view_as {
|
||||
ViewAs::Author => {}
|
||||
ViewAs::Moderator => {}
|
||||
ViewAs::AnotherUser => {
|
||||
// FIXME: Binding shouldn't require to_str().
|
||||
// Otherwise, it's calling the Display trait on the raw type which is resulting in an
|
||||
// incorrect binding and runtime error.
|
||||
query_builder.push(" AND m.creator_set_visibility = ");
|
||||
query_builder.push_bind(Visibility::Public.to_str());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(offset) = maybe_offset {
|
||||
if cursor_is_reversed {
|
||||
query_builder.push(format!(" AND m.id < {offset} "));
|
||||
} else {
|
||||
query_builder.push(format!(" AND m.id > {offset} "));
|
||||
}
|
||||
}
|
||||
|
||||
if cursor_is_reversed {
|
||||
query_builder.push(" ORDER BY m.id ASC ");
|
||||
} else {
|
||||
query_builder.push(" ORDER BY m.id DESC ");
|
||||
}
|
||||
|
||||
query_builder.push(format!(" LIMIT {limit} "));
|
||||
|
||||
query_builder
|
||||
}
|
||||
|
||||
struct MediaFileListItemInternal {
|
||||
id: i64,
|
||||
token: MediaFileToken,
|
||||
|
||||
origin_category: MediaFileOriginCategory,
|
||||
origin_product_category: MediaFileOriginProductCategory,
|
||||
maybe_origin_model_type: Option<MediaFileOriginModelType>,
|
||||
maybe_origin_model_token: Option<String>,
|
||||
|
||||
media_type: MediaFileType,
|
||||
public_bucket_directory_hash: String,
|
||||
maybe_public_bucket_prefix: Option<String>,
|
||||
maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
creator_set_visibility: Visibility,
|
||||
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
// NB(bt,2023-12-05): There's an issue with type hinting in the `as` clauses with QueryBuilder (or
|
||||
// raw query strings) and sqlx::FromRow, regardless of whether it is derived of manually
|
||||
// implemented. Perhaps this will improve in the future, but for now manually constructed queries
|
||||
// cannot have type hints, eg. the following:
|
||||
//
|
||||
// m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
// m.origin_category as `origin_category: enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory`,
|
||||
// m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
//
|
||||
// This results in the automatic mapping not being able to be found by name (for macro derive), and
|
||||
// in the manual case `row.try_get()` etc. won't have the correct column name (since the name is the
|
||||
// full "as" clause).
|
||||
impl FromRow<'_, MySqlRow> for MediaFileListItemInternal {
|
||||
fn from_row(row: &MySqlRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self {
|
||||
id: row.try_get("id")?,
|
||||
token: MediaFileToken::new(row.try_get("token")?),
|
||||
origin_category: MediaFileOriginCategory::try_from_mysql_row(row, "origin_category")?,
|
||||
origin_product_category: MediaFileOriginProductCategory::try_from_mysql_row(row, "origin_product_category")?,
|
||||
maybe_origin_model_type: MediaFileOriginModelType::try_from_mysql_row_nullable(row, "maybe_origin_model_type")?,
|
||||
maybe_origin_model_token: row.try_get("maybe_origin_model_token")?,
|
||||
media_type: MediaFileType::try_from_mysql_row(row, "media_type")?,
|
||||
public_bucket_directory_hash: row.try_get("public_bucket_directory_hash")?,
|
||||
maybe_public_bucket_prefix: row.try_get("maybe_public_bucket_prefix")?,
|
||||
maybe_public_bucket_extension: row.try_get("maybe_public_bucket_extension")?,
|
||||
creator_set_visibility: Visibility::try_from_mysql_row(row, "creator_set_visibility")?,
|
||||
created_at: row.try_get("created_at")?,
|
||||
updated_at: row.try_get("updated_at")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
pub mod get_media_file;
|
||||
pub mod insert_media_file_from_face_animation;
|
||||
pub mod insert_media_file_from_zero_shot_tts;
|
||||
pub mod insert_media_file_from_cli_tool;
|
||||
pub mod insert_media_file_from_voice_conversion;
|
||||
pub mod delete_media_file;
|
||||
pub mod get_media_file;
|
||||
pub mod insert_media_file_from_cli_tool;
|
||||
pub mod insert_media_file_from_face_animation;
|
||||
pub mod insert_media_file_from_voice_conversion;
|
||||
pub mod insert_media_file_from_zero_shot_tts;
|
||||
pub mod list_media_files;
|
||||
pub mod list_media_files_for_user;
|
||||
pub mod update_media_file;
|
||||
@@ -28,11 +28,6 @@ log = "0.4.14"
|
||||
once_cell = "1.9.0"
|
||||
regex = "1.5.4"
|
||||
|
||||
# SQlite Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
# ^^^ Granted, this applies to *MySQL* and probably not SQLite for our use case here ^^^
|
||||
sqlx = { version = "0.7.2", features = [ "sqlite", "runtime-tokio-native-tls", "chrono" ] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -16,10 +16,6 @@ path = "src/lib.rs"
|
||||
# Internal
|
||||
errors = { path = "../errors" }
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
|
||||
|
||||
@@ -20,7 +20,7 @@ user_input_common = { path = "../user_input_common" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
log = "0.4.14"
|
||||
once_cell = "1.18.0"
|
||||
regex = "1.5.4"
|
||||
|
||||
@@ -19,7 +19,7 @@ errors = { path = "../../lib/errors" }
|
||||
|
||||
# External
|
||||
bytes = "1.3"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
log = "0.4.14"
|
||||
once_cell = "1.18.0"
|
||||
strum = { version = "0.25", features = ["derive"] } # Fancy enum iteration, etc.
|
||||
|
||||
@@ -42,11 +42,6 @@ rand = "0.8.3"
|
||||
regex = "1.5.4"
|
||||
strum = { version = "0.24", features = ["derive"] } # Fancy enum iteration, etc.
|
||||
|
||||
# SQlite Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
# ^^^ Granted, this applies to *MySQL* and probably not SQLite for our use case here ^^^
|
||||
sqlx = { version = "0.7.2", features = [ "sqlite", "runtime-tokio-native-tls", "chrono" ] }
|
||||
|
||||
# Async Runtime
|
||||
|
||||
@@ -26,16 +26,12 @@ tokens = { path = "../../../_types/tokens" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
log = "0.4.14"
|
||||
rand = "0.8.5"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Runtime
|
||||
|
||||
@@ -35,7 +35,7 @@ subprocess_common = { path = "../../../lib/subprocess_common" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
log = "0.4.14"
|
||||
@@ -43,10 +43,6 @@ subprocess = "0.2.9"
|
||||
tempdir = "0.3.7"
|
||||
zip = "0.6.6"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis
|
||||
|
||||
@@ -46,7 +46,7 @@ tts_common = { path = "../../../lib/tts_common" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
@@ -56,10 +56,6 @@ once_cell = "1.18.0"
|
||||
subprocess = "0.2.9"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis
|
||||
|
||||
@@ -43,7 +43,7 @@ newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
lfu = "0.2.5"
|
||||
@@ -52,10 +52,6 @@ once_cell = "1.18.0"
|
||||
subprocess = "0.2.9"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis
|
||||
|
||||
@@ -28,17 +28,13 @@ mysql_queries = { path = "../../../lib/mysql_queries" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
log = "0.4.14"
|
||||
subprocess = "0.2.9"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis
|
||||
|
||||
@@ -32,7 +32,7 @@ newrelic-telemetry = { path = "../../../vendor/newrelic-telemetry-sdk-rust" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
clap = "2.34.0" # TODO: This is really out of date, but has conflicts when updated
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
@@ -41,10 +41,6 @@ log = "0.4.14"
|
||||
subprocess = "0.2.9"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis
|
||||
|
||||
@@ -28,17 +28,13 @@ mysql_queries = { path = "../../../lib/mysql_queries" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
log = "0.4.14"
|
||||
subprocess = "0.2.9"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis
|
||||
|
||||
@@ -26,17 +26,13 @@ mysql_queries = { path = "../../../lib/mysql_queries" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
log = "0.4.14"
|
||||
subprocess = "0.2.9"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis
|
||||
|
||||
@@ -28,7 +28,7 @@ twitch_common = { path = "../../../lib/api_clients/twitch_common" }
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.0"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
lazy_static = "1.4.0"
|
||||
@@ -47,11 +47,6 @@ futures = { version = "0.3.17", features = ["thread-pool"] } # feature for Threa
|
||||
futures-util = "0.3.29"
|
||||
futures_old_for_limiter = { package = "futures", version = "0.1.29" } # Older version of futures for `limiter` crate
|
||||
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis Database
|
||||
|
||||
@@ -24,7 +24,7 @@ url_config = { path = "../../../lib/url_config" }
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.0"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
log = "0.4.14"
|
||||
once_cell = "1.18.0"
|
||||
@@ -50,10 +50,6 @@ async-stripe = { version = "0.25.2", default-features = false, features = ["runt
|
||||
# include support for async fn in traits.
|
||||
async-trait = "0.1.58"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
my-workspace-hack = { version = "0.1", path = "../../../../my-workspace-hack" }
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ user_input_common = { path = "../../../lib/user_input_common" }
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.0"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
lazy_static = "1.4.0" # Regex caching (TODO: Use OnceCell instead)
|
||||
log = "0.4.14"
|
||||
@@ -58,10 +58,6 @@ serde_derive = "1.0.125"
|
||||
serde_json = "1.0.108"
|
||||
toml = "0.5.8"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# TODO: Shouldn't really be a dependency, but need to add caching for the DDoS attack.
|
||||
|
||||
@@ -21,7 +21,7 @@ errors = { path = "../../../lib/errors" }
|
||||
reusable_types = { path = "../../../_types/reusable_types" }
|
||||
|
||||
# External
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
#once_cell = "1.9.0"
|
||||
|
||||
@@ -13,6 +13,10 @@ publish = false
|
||||
name = "storyteller-web"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "docs"
|
||||
path = "src/api_doc_server.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Internal
|
||||
@@ -54,7 +58,7 @@ users_component = { path = "../../plugins/users" }
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.0"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
derive_more = "0.99.17"
|
||||
dotenv = "0.15.0"
|
||||
hex = "0.4.3"
|
||||
@@ -97,10 +101,6 @@ futures_old_for_limiter = { package = "futures", version = "0.1.29" } # Older ve
|
||||
futures-core = "0.3.29" # NB: For middleware/futures upgrade
|
||||
pin-project-lite = "0.2.9" # NB: For middleware/futures upgrade
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis Database
|
||||
@@ -160,6 +160,8 @@ pulldown-cmark = "0.9.3"
|
||||
# include support for async fn in traits.
|
||||
async-trait = "0.1.58"
|
||||
my-workspace-hack = { version = "0.1", path = "../../../../my-workspace-hack" }
|
||||
utoipa = { version = "4", features = ["actix_extras"] }
|
||||
utoipa-swagger-ui = { version = "5", features = ["actix-web"] }
|
||||
|
||||
[build-dependencies]
|
||||
# NB: Only using diesel for migrations support
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
use crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::EnqueueTTSRequest;
|
||||
use crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::EnqueueTTSRequestSuccessResponse;
|
||||
use crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::EnqueueTTSRequestError;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
|
||||
use utoipa::OpenApi;
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(paths(crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::enqueue_tts_request),
|
||||
components(schemas(EnqueueTTSRequest,EnqueueTTSRequestSuccessResponse,EnqueueTTSRequestError,InferenceJobToken)))]
|
||||
pub struct ApiDoc;
|
||||
@@ -0,0 +1,73 @@
|
||||
// Never allow these
|
||||
#![forbid(private_in_public)]
|
||||
#![forbid(unused_must_use)] // NB: It's unsafe to not close/check some things
|
||||
|
||||
// Okay to toggle
|
||||
//#![forbid(warnings)]
|
||||
#![allow(unreachable_patterns)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_mut)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
// Always allow
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[macro_use] extern crate lazy_static;
|
||||
#[macro_use] extern crate magic_crypt;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
use actix_web::{App, HttpServer, middleware, web};
|
||||
use actix_web::middleware::{DefaultHeaders, Logger};
|
||||
|
||||
use r2d2_redis::r2d2;
|
||||
use r2d2_redis::redis::Commands;
|
||||
use r2d2_redis::RedisConnectionManager;
|
||||
use sqlx::mysql::MySqlPoolOptions;
|
||||
use sqlx::MySqlPool;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
|
||||
use crate::configs::static_api_tokens::{StaticApiTokenConfig, StaticApiTokens, StaticApiTokenSet};
|
||||
use crate::server_state::{DurableInMemoryCaches, EnvConfig, EphemeralInMemoryCaches, InMemoryCaches, RedisRateLimiters, ServerInfo, ServerState, StaticFeatureFlags, StripeSettings, TrollBans, TwitchOauth, TwitchOauthSecrets};
|
||||
|
||||
pub mod billing;
|
||||
pub mod configs;
|
||||
pub mod cookies;
|
||||
pub mod http_server;
|
||||
pub mod memory_cache;
|
||||
pub mod model;
|
||||
pub mod routes;
|
||||
pub mod server_state;
|
||||
pub mod subscriptions;
|
||||
pub mod threads;
|
||||
pub mod user_avatars;
|
||||
pub mod util;
|
||||
pub mod validations;
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
future::{self, Ready},
|
||||
net::Ipv4Addr,
|
||||
};
|
||||
|
||||
pub mod api_doc;
|
||||
use api_doc::ApiDoc;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use utoipa::OpenApi;
|
||||
#[actix_web::main]
|
||||
async fn main() -> Result<(), impl Error> {
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.service(
|
||||
SwaggerUi::new("/{_:.*}")
|
||||
.url("/api-docs/openapi.json", ApiDoc::openapi()),
|
||||
)
|
||||
})
|
||||
.bind(("localhost", 8989)).unwrap()
|
||||
.run().await
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
pub mod pagination_cursors;
|
||||
pub mod pagination_page;
|
||||
pub mod user_details_lite;
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/// Pagination by cursors
|
||||
/// These types of pagination are for "infinite scrolling", which do not reveal a number of pages.
|
||||
/// This is good so that investors and competitors cannot reveal how many database records we have.
|
||||
/// This is typically used for "discovery" type pages, not user profiles.
|
||||
///
|
||||
/// See `PaginationPage` for the other type of pagination.
|
||||
#[derive(Serialize)]
|
||||
pub struct PaginationCursors {
|
||||
|
||||
/// The "next" cursor.
|
||||
/// This is an opaque (typically even encrypted) handle.
|
||||
pub maybe_next: Option<String>,
|
||||
|
||||
/// The "previous" cursor.
|
||||
/// This is an opaque (typically even encrypted) handle.
|
||||
pub maybe_previous: Option<String>,
|
||||
|
||||
/// Details whether we're walking forward or backward.
|
||||
pub cursor_is_reversed: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
|
||||
#[test]
|
||||
fn test_serialize_empty_json() {
|
||||
// Make sure this interface doesn't change.
|
||||
let pagination = PaginationCursors {
|
||||
maybe_next: None,
|
||||
maybe_previous: None,
|
||||
cursor_is_reversed: false,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&pagination).expect("serialization");
|
||||
|
||||
assert_eq!(json, "{\"maybe_next\":null,\"maybe_previous\":null,\"cursor_is_reversed\":false}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_full_json() {
|
||||
// Make sure this interface doesn't change.
|
||||
let pagination = PaginationCursors {
|
||||
maybe_next: Some("foo".to_string()),
|
||||
maybe_previous: Some("bar".to_string()),
|
||||
cursor_is_reversed: true,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&pagination).expect("serialization");
|
||||
|
||||
assert_eq!(json, "{\"maybe_next\":\"foo\",\"maybe_previous\":\"bar\",\"cursor_is_reversed\":true}")
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/// Pagination by page id
|
||||
/// This type of pagination is by "page id" and "page count".
|
||||
/// This should never be used to walk the entire public database as competitors and investors
|
||||
/// can use it to glean information about the scale of our service.
|
||||
/// This is best used for user profiles and scoped down results.
|
||||
///
|
||||
/// See `PaginationCursors` for the other type of pagination.
|
||||
#[derive(Serialize)]
|
||||
pub struct PaginationPage {
|
||||
/// The current page number
|
||||
pub current: u64,
|
||||
|
||||
/// How many pages there are in total
|
||||
pub total_page_count: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::http_server::common_responses::pagination_page::PaginationPage;
|
||||
|
||||
#[test]
|
||||
fn test_serialize() {
|
||||
let pagination = PaginationPage {
|
||||
current: 5,
|
||||
total_page_count: 123,
|
||||
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&pagination).expect("serialization");
|
||||
|
||||
assert_eq!(json, "{\"current\":5,\"total_page_count\":123}")
|
||||
}
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::{HttpMessage, HttpRequest, HttpResponse, web};
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Path, Query};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{info, warn};
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use mysql_queries::queries::media_files::list_media_files::{list_media_files, ListMediaFilesArgs, ViewAs};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
use crate::http_server::common_responses::user_details_lite::UserDetailsLight;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct QueryParams {
|
||||
pub sort_ascending: Option<bool>,
|
||||
pub per_page: Option<usize>,
|
||||
pub cursor: Option<String>,
|
||||
pub cursor_is_reversed: Option<bool>,
|
||||
pub filter_media_type: Option<MediaFileType>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SuccessResponse {
|
||||
pub success: bool,
|
||||
pub results: Vec<MediaFileListItem>,
|
||||
pub pagination: PaginationCursors,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MediaFileListItem {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub origin_category: MediaFileOriginCategory,
|
||||
pub origin_product_category: MediaFileOriginProductCategory,
|
||||
pub maybe_origin_model_type: Option<MediaFileOriginModelType>,
|
||||
pub maybe_origin_model_token: Option<String>,
|
||||
|
||||
pub media_type: MediaFileType,
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
pub maybe_creator: Option<UserDetailsLight>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorResponse {
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for ErrorResponse {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
ErrorResponse::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
ErrorResponse::ServerError => "server error".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ErrorResponse {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<QueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, ErrorResponse>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session(&http_request, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
|
||||
let mut is_mod = false;
|
||||
|
||||
match maybe_user_session {
|
||||
None => {},
|
||||
Some(session) => {
|
||||
is_mod = session.can_ban_users;
|
||||
},
|
||||
};
|
||||
|
||||
// TODO(bt,2023-12-04): Enforce real maximums and defaults
|
||||
let limit = query.per_page.unwrap_or(25);
|
||||
|
||||
let sort_ascending = query.sort_ascending.unwrap_or(false);
|
||||
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
|
||||
|
||||
let cursor = if let Some(cursor) = query.cursor.as_deref() {
|
||||
let cursor = server_state.sort_key_crypto.decrypt_id(cursor)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
Some(cursor as usize)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let view_as = if is_mod {
|
||||
ViewAs::Moderator
|
||||
} else {
|
||||
ViewAs::NonModerator
|
||||
};
|
||||
|
||||
let query_results = list_media_files(ListMediaFilesArgs {
|
||||
limit,
|
||||
maybe_filter_media_type: query.filter_media_type,
|
||||
maybe_offset: cursor,
|
||||
cursor_is_reversed,
|
||||
view_as,
|
||||
mysql_pool: &server_state.mysql_pool,
|
||||
}).await;
|
||||
|
||||
|
||||
let results_page = match query_results {
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(ErrorResponse::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
let cursor_next = if let Some(id) = results_page.last_id {
|
||||
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
Some(cursor)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cursor_previous = if let Some(id) = results_page.first_id {
|
||||
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
Some(cursor)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let results = results_page.records.into_iter()
|
||||
.map(|record| MediaFileListItem {
|
||||
token: record.token,
|
||||
origin_category: record.origin_category,
|
||||
origin_product_category: record.origin_product_category,
|
||||
maybe_origin_model_type: record.maybe_origin_model_type,
|
||||
maybe_origin_model_token: record.maybe_origin_model_token,
|
||||
media_type: record.media_type,
|
||||
public_bucket_directory_hash: record.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: record.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: record.maybe_public_bucket_extension,
|
||||
maybe_creator: UserDetailsLight::from_optional_db_fields_owned(
|
||||
record.maybe_creator_user_token,
|
||||
record.maybe_creator_username,
|
||||
record.maybe_creator_display_name,
|
||||
record.maybe_creator_gravatar_hash,
|
||||
),
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let response = SuccessResponse {
|
||||
success: true,
|
||||
results,
|
||||
pagination: PaginationCursors {
|
||||
maybe_next: cursor_next,
|
||||
maybe_previous: cursor_previous,
|
||||
cursor_is_reversed,
|
||||
}
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| ErrorResponse::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(body))
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::{HttpMessage, HttpRequest, HttpResponse, web};
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Path, Query};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{info, warn};
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use mysql_queries::queries::media_files::list_media_files_for_user::{list_media_files_for_user, ListMediaFileForUserArgs, ViewAs};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::server_state::ServerState;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PathInfo {
|
||||
username: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct QueryParams {
|
||||
pub sort_ascending: Option<bool>,
|
||||
pub limit: Option<usize>,
|
||||
pub cursor: Option<String>,
|
||||
pub cursor_is_reversed: Option<bool>,
|
||||
pub filter_media_type: Option<MediaFileType>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SuccessResponse {
|
||||
pub success: bool,
|
||||
pub results: Vec<MediaFileListItem>,
|
||||
pub pagination: PaginationCursors,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MediaFileListItem {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub origin_category: MediaFileOriginCategory,
|
||||
pub origin_product_category: MediaFileOriginProductCategory,
|
||||
pub maybe_origin_model_type: Option<MediaFileOriginModelType>,
|
||||
pub maybe_origin_model_token: Option<String>,
|
||||
|
||||
pub media_type: MediaFileType,
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorResponse {
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for ErrorResponse {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
ErrorResponse::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
ErrorResponse::ServerError => "server error".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ErrorResponse {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_media_files_for_user_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<PathInfo>,
|
||||
query: Query<QueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, ErrorResponse>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session(&http_request, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
|
||||
let mut is_author = false;
|
||||
let mut is_mod = false;
|
||||
|
||||
match maybe_user_session {
|
||||
None => {},
|
||||
Some(session) => {
|
||||
is_author = session.username == path.username;
|
||||
is_mod = session.can_ban_users;
|
||||
},
|
||||
};
|
||||
|
||||
// TODO(bt,2023-12-04): Enforce real maximums and defaults
|
||||
let limit = query.limit.unwrap_or(25);
|
||||
|
||||
let sort_ascending = query.sort_ascending.unwrap_or(false);
|
||||
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
|
||||
|
||||
let cursor = if let Some(cursor) = query.cursor.as_deref() {
|
||||
let cursor = server_state.sort_key_crypto.decrypt_id(cursor)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
Some(cursor as usize)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let view_as = if is_author {
|
||||
ViewAs::Author
|
||||
} else if is_mod {
|
||||
ViewAs::Moderator
|
||||
} else {
|
||||
ViewAs::AnotherUser
|
||||
};
|
||||
|
||||
let query_results = list_media_files_for_user(ListMediaFileForUserArgs {
|
||||
username: &path.username,
|
||||
limit,
|
||||
maybe_filter_media_type: query.filter_media_type,
|
||||
maybe_offset: cursor,
|
||||
cursor_is_reversed,
|
||||
view_as,
|
||||
mysql_pool: &server_state.mysql_pool,
|
||||
}).await;
|
||||
|
||||
|
||||
let results_page = match query_results {
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(ErrorResponse::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
let cursor_next = if let Some(id) = results_page.last_id {
|
||||
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
Some(cursor)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cursor_previous = if let Some(id) = results_page.first_id {
|
||||
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
|
||||
.map_err(|e| {
|
||||
warn!("crypto error: {:?}", e);
|
||||
ErrorResponse::ServerError
|
||||
})?;
|
||||
Some(cursor)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let results = results_page.records.into_iter()
|
||||
.map(|record| MediaFileListItem {
|
||||
token: record.token,
|
||||
origin_category: record.origin_category,
|
||||
origin_product_category: record.origin_product_category,
|
||||
maybe_origin_model_type: record.maybe_origin_model_type,
|
||||
maybe_origin_model_token: record.maybe_origin_model_token,
|
||||
media_type: record.media_type,
|
||||
public_bucket_directory_hash: record.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: record.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: record.maybe_public_bucket_extension,
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let response = SuccessResponse {
|
||||
success: true,
|
||||
results,
|
||||
pagination: PaginationCursors {
|
||||
maybe_next: cursor_next,
|
||||
maybe_previous: cursor_previous,
|
||||
cursor_is_reversed,
|
||||
}
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| ErrorResponse::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(body))
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod delete_media_file;
|
||||
pub mod drain_multipart_request;
|
||||
pub mod get_media_file;
|
||||
pub mod delete_media_file;
|
||||
pub mod list_media_files;
|
||||
pub mod list_media_files_for_user;
|
||||
+20
-3
@@ -1,8 +1,8 @@
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
use utoipa::ToSchema;
|
||||
|
||||
// TODO MOVE into own file.
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -42,7 +42,9 @@ const DEBUG_HEADER_NAME: &str = "enable-debug-mode";
|
||||
/// This is useful for catching the live logs or intercepting the job.
|
||||
const ROUTING_TAG_HEADER_NAME: &str = "routing-tag";
|
||||
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(ToSchema)]
|
||||
pub struct EnqueueTTSRequest {
|
||||
uuid_idempotency_token: String,
|
||||
text: String,
|
||||
@@ -50,12 +52,14 @@ pub struct EnqueueTTSRequest {
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(ToSchema)]
|
||||
pub struct EnqueueTTSRequestSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(ToSchema)]
|
||||
pub enum EnqueueTTSRequestError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
@@ -95,13 +99,26 @@ impl std::fmt::Display for EnqueueTTSRequestError {
|
||||
// Reference enqueue_infer_tts_handler.rs for checks: rate limiting / user sessions
|
||||
// insert generic inference job.rs
|
||||
// Need to convert it to generic inference job.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/inference/enqueue_tts/",
|
||||
responses(
|
||||
(status = 200, description = "Enqueue TTS generically", body = EnqueueTTSRequestSuccessResponse),
|
||||
(status = 400, description = "Bad input", body = EnqueueTTSRequestError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueTTSRequestError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueTTSRequestError),
|
||||
(status = 500, description = "Server error", body = EnqueueTTSRequestError)
|
||||
),
|
||||
params(
|
||||
("request" = EnqueueTTSRequest, description = "Payload for TTS Request")
|
||||
)
|
||||
)]
|
||||
pub async fn enqueue_tts_request(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<EnqueueTTSRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, EnqueueTTSRequestError> {
|
||||
println!("Received payload");
|
||||
|
||||
|
||||
let mut maybe_user_token: Option<UserToken> = None;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await.map_err(|err| {
|
||||
|
||||
@@ -44,6 +44,8 @@ use crate::http_server::endpoints::investor_demo::enable_demo_mode_handler::enab
|
||||
use crate::http_server::endpoints::leaderboard::get_leaderboard::leaderboard_handler;
|
||||
use crate::http_server::endpoints::media_files::delete_media_file::delete_media_file_handler;
|
||||
use crate::http_server::endpoints::media_files::get_media_file::get_media_file_handler;
|
||||
use crate::http_server::endpoints::media_files::list_media_files::list_media_files_handler;
|
||||
use crate::http_server::endpoints::media_files::list_media_files_for_user::list_media_files_for_user_handler;
|
||||
use crate::http_server::endpoints::media_uploads::list_user_media_uploads_of_type::list_user_media_uploads_of_type_handler;
|
||||
use crate::http_server::endpoints::media_uploads::upload_audio::upload_audio_handler;
|
||||
use crate::http_server::endpoints::media_uploads::upload_image::upload_image_handler;
|
||||
@@ -168,7 +170,6 @@ pub fn add_routes<T, B> (app: App<T>, server_environment: ServerEnvironment) ->
|
||||
InitError = (),
|
||||
>,
|
||||
{
|
||||
|
||||
let mut app = add_moderator_routes(app); /* /moderation */
|
||||
app = add_tts_routes(app); /* /tts */
|
||||
app = add_w2l_routes(app); /* /w2l */
|
||||
@@ -1016,6 +1017,14 @@ fn add_media_file_routes<T, B> (app: App<T>) -> App<T>
|
||||
.route(web::delete().to(delete_media_file_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/list")
|
||||
.route(web::get().to(list_media_files_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/list/user/{username}")
|
||||
.route(web::get().to(list_media_files_for_user_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ twitch_common = { path = "../../../lib/api_clients/twitch_common" }
|
||||
# External
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.0"
|
||||
chrono = { version = "=0.4.22", features = ["serde"] }
|
||||
chrono = { version = "=0.4.31", features = ["serde"] }
|
||||
derive_more = "0.99.17"
|
||||
dotenv = "0.15.0"
|
||||
hostname = "0.3.1"
|
||||
@@ -47,10 +47,6 @@ futures = { version = "0.3.17", features = ["thread-pool"] } # feature for Threa
|
||||
futures-util = "0.3.29"
|
||||
futures_old_for_limiter = { package = "futures", version = "0.1.29" } # Older version of futures for `limiter` crate
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
|
||||
|
||||
# Redis Database
|
||||
|
||||
@@ -22,7 +22,7 @@ bitflags = { version = "2.4.1", default-features = false, features = ["serde", "
|
||||
block-buffer = { version = "0.9.0", default-features = false, features = ["block-padding"] }
|
||||
bytemuck = { version = "1.14.0", default-features = false, features = ["extern_crate_alloc"] }
|
||||
byteorder = { version = "1.5.0" }
|
||||
chrono = { version = "0.4.22", features = ["alloc", "serde"] }
|
||||
chrono = { version = "0.4.31", features = ["alloc", "serde"] }
|
||||
combine = { version = "4.6.6", default-features = false, features = ["std", "tokio"] }
|
||||
crossbeam-utils = { version = "0.8.16" }
|
||||
deranged = { version = "0.3.9", default-features = false, features = ["powerfmt", "serde", "std"] }
|
||||
@@ -88,7 +88,7 @@ base64ct = { version = "1.6.0", default-features = false, features = ["alloc"] }
|
||||
bitflags = { version = "2.4.1", default-features = false, features = ["serde", "std"] }
|
||||
byteorder = { version = "1.5.0" }
|
||||
cc = { version = "1.0.83", default-features = false, features = ["parallel"] }
|
||||
chrono = { version = "0.4.22", features = ["alloc", "serde"] }
|
||||
chrono = { version = "0.4.31", features = ["alloc", "serde"] }
|
||||
crossbeam-utils = { version = "0.8.16" }
|
||||
derive_more = { version = "0.99.17" }
|
||||
digest = { version = "0.10.7", features = ["mac", "oid", "std"] }
|
||||
@@ -142,22 +142,22 @@ url = { version = "2.4.1", features = ["serde"] }
|
||||
[target.x86_64-unknown-linux-gnu.dependencies]
|
||||
openssl = { version = "0.10.59", features = ["vendored"] }
|
||||
openssl-sys = { version = "0.9.95", default-features = false, features = ["vendored"] }
|
||||
rustix = { version = "0.38.25", features = ["fs", "termios"] }
|
||||
rustix = { version = "0.38.26", features = ["fs", "termios"] }
|
||||
spin = { version = "0.9.8" }
|
||||
|
||||
[target.x86_64-unknown-linux-gnu.build-dependencies]
|
||||
openssl = { version = "0.10.59", features = ["vendored"] }
|
||||
openssl-sys = { version = "0.9.95", default-features = false, features = ["vendored"] }
|
||||
rustix = { version = "0.38.25", features = ["fs", "termios"] }
|
||||
rustix = { version = "0.38.26", features = ["fs", "termios"] }
|
||||
socket2 = { version = "0.5.5", default-features = false, features = ["all"] }
|
||||
spin = { version = "0.9.8" }
|
||||
|
||||
[target.x86_64-apple-darwin.dependencies]
|
||||
rustix = { version = "0.38.25", features = ["fs", "termios"] }
|
||||
rustix = { version = "0.38.26", features = ["fs", "termios"] }
|
||||
spin = { version = "0.9.8" }
|
||||
|
||||
[target.x86_64-apple-darwin.build-dependencies]
|
||||
rustix = { version = "0.38.25", features = ["fs", "termios"] }
|
||||
rustix = { version = "0.38.26", features = ["fs", "termios"] }
|
||||
socket2 = { version = "0.5.5", default-features = false, features = ["all"] }
|
||||
spin = { version = "0.9.8" }
|
||||
|
||||
|
||||
Reference in new issue
Block a user