diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 792f395..8c0c5e3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,6 +10,7 @@ permissions: env: RUSTFLAGS: -Dwarnings + CARGO_NET_GIT_FETCH_WITH_CLI: true jobs: test: @@ -33,10 +34,16 @@ jobs: with: toolchain: ${{matrix.rust}} components: clippy, rustfmt + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: work around Cargo.lock filetime not being updated..? run: touch Cargo.lock - - run: make ci + - run: ENABLE_GSFS=0 make update + - run: ENABLE_GSFS=0 make ci - - run: make push + - run: ENABLE_GSFS=1 make update + - run: ENABLE_GSFS=1 make ci + - run: ENABLE_GSFS=1 make push diff --git a/.github/workflows/pr-ci.yaml b/.github/workflows/pr-ci.yaml index 16fff11..7a1ba24 100644 --- a/.github/workflows/pr-ci.yaml +++ b/.github/workflows/pr-ci.yaml @@ -32,8 +32,15 @@ jobs: with: toolchain: ${{matrix.rust}} components: clippy, rustfmt + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: work around Cargo.lock filetime not being updated..? run: touch Cargo.lock - - run: make ci + - run: ENABLE_GSFS=0 make update + - run: ENABLE_GSFS=0 make ci + + - run: ENABLE_GSFS=1 make update + - run: ENABLE_GSFS=1 make ci diff --git a/Cargo.lock b/Cargo.lock index a98d5b5..709b845 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,7 +53,7 @@ dependencies = [ "actix-service", "actix-tls", "actix-utils", - "ahash", + "ahash 0.8.11", "base64", "bitflags 2.8.0", "brotli", @@ -74,7 +74,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rand 0.8.5", - "sha1 0.10.6", + "sha1", "smallvec", "tokio", "tokio-util", @@ -89,7 +89,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -127,7 +127,7 @@ dependencies = [ "parse-size", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -243,7 +243,7 @@ dependencies = [ "actix-tls", "actix-utils", "actix-web-codegen", - "ahash", + "ahash 0.8.11", "bytes", "bytestring", "cfg-if", @@ -266,7 +266,7 @@ dependencies = [ "serde_urlencoded", "smallvec", "socket2", - "time 0.3.37", + "time", "url", ] @@ -279,7 +279,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -297,6 +297,27 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.11" @@ -340,6 +361,71 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.95" @@ -374,18 +460,27 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", ] [[package]] @@ -434,6 +529,107 @@ dependencies = [ "tokio", ] +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core 0.4.5", + "bytes", + "futures-util", + "http 1.2.0", + "http-body", + "http-body-util", + "itoa", + "matchit 0.7.3", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.2", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" +dependencies = [ + "axum-core 0.5.0", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.2.0", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.2.0", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backblaze" version = "0.1.0" @@ -448,7 +644,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sha1 0.10.6", + "sha1", "thiserror 2.0.11", "tokio", "tokio-util", @@ -470,18 +666,18 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bb8" version = "0.9.0" @@ -489,7 +685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d8b8e1a22743d9241575c6ba822cf9c8fef34771c86ab7e477a4fbfd254e5" dependencies = [ "futures-util", - "parking_lot", + "parking_lot 0.12.3", "tokio", ] @@ -504,6 +700,15 @@ dependencies = [ "tokio-postgres", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.71.1" @@ -513,7 +718,7 @@ dependencies = [ "bitflags 2.8.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", @@ -521,7 +726,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -535,6 +740,9 @@ name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -670,9 +878,12 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +dependencies = [ + "serde", +] [[package]] name = "bytestring" @@ -689,7 +900,7 @@ version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" dependencies = [ - "ahash", + "ahash 0.8.11", "async-trait", "cached_proc_macro", "cached_proc_macro_types", @@ -710,7 +921,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -721,9 +932,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "jobserver", "libc", @@ -745,6 +956,54 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.6", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -756,6 +1015,52 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.5.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "common" version = "0.1.0" @@ -780,10 +1085,19 @@ dependencies = [ ] [[package]] -name = "const_fn" -version = "0.4.11" +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "convert_case" @@ -798,10 +1112,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "percent-encoding", - "time 0.3.37", + "time", "version_check", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -835,6 +1155,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -842,6 +1177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -866,7 +1202,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -877,7 +1213,24 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[package]] +name = "data-encoding" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", ] [[package]] @@ -887,6 +1240,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -907,7 +1261,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -917,20 +1271,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.1", - "syn 2.0.96", + "rustc_version", + "syn 2.0.98", ] [[package]] @@ -940,16 +1294,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "displaydoc" version = "0.2.5" @@ -958,27 +1307,74 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] -name = "either" -version = "1.13.0" +name = "dotenvy" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "dtoa" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] -name = "env_logger" -version = "0.8.4" +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "env_logger" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ @@ -1002,6 +1398,28 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1033,12 +1451,29 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1105,6 +1540,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.12.3", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -1119,7 +1565,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1197,6 +1643,48 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "gsfs" +version = "0.1.0" +source = "git+ssh://git@github.com/zzlk/gsfs.git?tag=scmscx.com#aa0d2933a2e7ba02f2a1a4a96d627a7568345b9d" +dependencies = [ + "anyhow", + "async-stream", + "axum 0.8.1", + "bytes", + "chacha20poly1305", + "clap", + "crc", + "data-encoding", + "fnv", + "futures", + "lazy_static", + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-otlp", + "opentelemetry-stdout", + "opentelemetry_sdk", + "prometheus-client", + "rand 0.9.0", + "rand_core 0.9.0", + "reed-solomon-erasure", + "regex", + "serde", + "serde_json", + "serde_json5", + "serde_with", + "sqlx", + "tarpc", + "thiserror 2.0.11", + "tokio", + "tokio-util", + "tracing", + "tracing-opentelemetry", + "tracing-subscriber", + "zerocopy 0.8.17", + "zstd", +] + [[package]] name = "h2" version = "0.3.26" @@ -1209,7 +1697,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -1228,7 +1716,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.2.0", - "indexmap", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -1252,9 +1740,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.3.0" +version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9" +checksum = "d752747ddabc4c1a70dd28e72f2e3c218a816773e0d7faf67433f1acfa6cba7c" dependencies = [ "derive_builder", "log", @@ -1267,13 +1755,22 @@ dependencies = [ "walkdir", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.11", "allocator-api2", ] @@ -1282,6 +1779,41 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] [[package]] name = "hmac" @@ -1292,6 +1824,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "http" version = "0.2.12" @@ -1355,6 +1896,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "1.6.0" @@ -1368,6 +1915,7 @@ dependencies = [ "http 1.2.0", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1392,6 +1940,19 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.10" @@ -1411,6 +1972,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -1526,7 +2110,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1574,6 +2158,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -1582,6 +2177,25 @@ checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", ] [[package]] @@ -1590,6 +2204,12 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.13.0" @@ -1599,6 +2219,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -1635,6 +2264,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] [[package]] name = "libc" @@ -1652,6 +2284,23 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1697,6 +2346,15 @@ version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1706,6 +2364,18 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -1746,9 +2416,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" dependencies = [ "adler2", "simd-adler32", @@ -1786,12 +2456,49 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-modular" version = "0.6.1" @@ -1814,59 +2521,175 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "openssl-sys" +version = "0.9.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "opentelemetry" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570074cc999d1a58184080966e5bd3bf3a9a4af650c3b05047c2621e7405cd17" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror 1.0.69", +] + +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f62d9a23c680ab91c74605f5006110768eb67600bb654937fef5c852fb8ec7" +dependencies = [ + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29e1f9c8b032d4f635c730c0efcf731d5e2530ea13fa8bef7939ddc8420696bd" +dependencies = [ + "async-trait", + "futures-core", + "http 1.2.0", + "opentelemetry", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "thiserror 1.0.69", + "tokio", + "tonic", ] [[package]] -name = "object" -version = "0.36.7" +name = "opentelemetry-proto" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "c9d3968ce3aefdcca5c27e3c4ea4391b37547726a70893aab52d3de95d5f8b34" dependencies = [ - "memchr", + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", ] [[package]] -name = "once_cell" -version = "1.20.2" +name = "opentelemetry-semantic-conventions" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "1cefe0543875379e47eb5f1e68ff83f45cc41366a92dfd0d073d513bf68e9a05" [[package]] -name = "openssl" -version = "0.10.69" +name = "opentelemetry-stdout" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +checksum = "4a5c185afbc26decc6f10d728971847e3f96532d2b3fb7db2569c1bce154509b" dependencies = [ - "bitflags 2.8.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", + "async-trait", + "chrono", + "futures-util", + "opentelemetry", + "opentelemetry_sdk", + "ordered-float", + "serde", + "serde_json", + "thiserror 1.0.69", ] [[package]] -name = "openssl-macros" -version = "0.1.1" +name = "opentelemetry_sdk" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "d2c627d9f4c9cdc1f21a29ee4bfbd6028fcb8bcf2a857b43f3abdf72c9c862f3" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", + "async-trait", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry", + "percent-encoding", + "rand 0.8.5", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-stream", ] [[package]] -name = "openssl-sys" -version = "0.9.104" +name = "ordered-float" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "num-traits", ] [[package]] @@ -1875,6 +2698,23 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1882,7 +2722,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -1893,7 +2747,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -1910,6 +2764,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1947,7 +2810,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2026,7 +2889,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2049,22 +2912,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2079,6 +2942,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.31" @@ -2098,11 +2982,22 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "postgres-protocol" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" dependencies = [ "base64", "byteorder", @@ -2111,16 +3006,16 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand 0.8.5", + "rand 0.9.0", "sha2", "stringprep", ] [[package]] name = "postgres-types" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" dependencies = [ "bytes", "fallible-iterator", @@ -2149,7 +3044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2167,6 +3062,52 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prometheus-client" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf41c1a7c32ed72abe5082fb19505b969095c12da9f5732a4bc9878757fd087c" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.3", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "quickcheck" version = "1.0.3" @@ -2217,7 +3158,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.0", - "zerocopy 0.8.14", + "zerocopy 0.8.17", ] [[package]] @@ -2256,7 +3197,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.14", + "zerocopy 0.8.17", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -2268,6 +3218,19 @@ dependencies = [ "bitflags 2.8.0", ] +[[package]] +name = "reed-solomon-erasure" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7263373d500d4d4f505d43a2a662d475a894aa94503a1ee28e9188b5f3960d4f" +dependencies = [ + "libm", + "lru", + "parking_lot 0.11.2", + "smallvec", + "spin 0.9.8", +] + [[package]] name = "regex" version = "1.11.1" @@ -2350,7 +3313,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "tokio", "tokio-rustls 0.25.0", "tokio-util", @@ -2360,7 +3323,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", "winreg", ] @@ -2394,6 +3357,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2402,18 +3385,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" - -[[package]] -name = "rustc_version" -version = "0.2.3" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -2421,7 +3395,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.25", + "semver", ] [[package]] @@ -2534,22 +3508,27 @@ dependencies = [ "futures", "futures-core", "futures-util", + "gsfs", "handlebars", "image", "lazy_static", "log", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry-stdout", + "opentelemetry_sdk", "rand 0.9.0", "regex", "reqwest", "serde", "serde_json", - "sha1 0.10.6", + "sha1", "sha2", - "time 0.2.27", "tokio", "tokio-util", "tracing", "tracing-log", + "tracing-opentelemetry", "tracing-subscriber", "uuid", "walkdir", @@ -2572,27 +3551,12 @@ dependencies = [ "untrusted 0.9.0", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.217" @@ -2610,7 +3574,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2625,6 +3589,27 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_json5" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a6b754515e1a7bd79fc2edeaecee526fc80cb3a918607e5ca149225a3a9586" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_plain" version = "1.0.2" @@ -2647,12 +3632,33 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.6.1" +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.1", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ - "sha1_smol", + "darling", + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -2666,12 +3672,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.10.8" @@ -2707,6 +3707,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -2739,6 +3749,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -2761,70 +3774,218 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] -name = "stable_deref_trait" -version = "1.2.0" +name = "spki" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" +dependencies = [ + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.2", + "hashlink", + "indexmap 2.7.1", + "log", + "memchr", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.11", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.98", +] [[package]] -name = "standback" -version = "0.2.17" +name = "sqlx-macros-core" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ - "version_check", + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.98", + "tempfile", + "tokio", + "url", ] [[package]] -name = "stdweb" -version = "0.4.20" +name = "sqlx-mysql" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", + "atoi", + "base64", + "bitflags 2.8.0", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "tracing", + "whoami", ] [[package]] -name = "stdweb-derive" -version = "0.5.3" +name = "sqlx-postgres" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" dependencies = [ - "proc-macro2", - "quote", + "atoi", + "base64", + "bitflags 2.8.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", "serde", - "serde_derive", - "syn 1.0.109", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "tracing", + "whoami", ] [[package]] -name = "stdweb-internal-macros" -version = "0.2.9" +name = "sqlx-sqlite" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" dependencies = [ - "base-x", - "proc-macro2", - "quote", + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", "serde", - "serde_derive", - "serde_json", - "sha1 0.6.1", - "syn 1.0.109", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", ] [[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stormlib-bindings" @@ -2869,9 +4030,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2884,6 +4045,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synstructure" version = "0.13.1" @@ -2892,7 +4059,43 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[package]] +name = "tarpc" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d1be17be018ebeec4c489449adb5ef227746775974c311ce79e09886ef83c7" +dependencies = [ + "anyhow", + "fnv", + "futures", + "humantime", + "opentelemetry", + "opentelemetry-semantic-conventions", + "pin-project", + "rand 0.8.5", + "serde", + "static_assertions", + "tarpc-plugins", + "thiserror 1.0.69", + "tokio", + "tokio-serde", + "tokio-util", + "tracing", + "tracing-opentelemetry", +] + +[[package]] +name = "tarpc-plugins" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e3d9676af494694e11a3e367a4bfa7f6d1d5566bd0fe9aceb4aa9281122ab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -2935,7 +4138,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2946,7 +4149,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2959,21 +4162,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros 0.1.1", - "version_check", - "winapi", -] - [[package]] name = "time" version = "0.3.37" @@ -2986,7 +4174,7 @@ dependencies = [ "powerfmt", "serde", "time-core", - "time-macros 0.2.19", + "time-macros", ] [[package]] @@ -2995,16 +4183,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - [[package]] name = "time-macros" version = "0.2.19" @@ -3015,19 +4193,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - [[package]] name = "tinystr" version = "0.7.6" @@ -3063,7 +4228,7 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", @@ -3080,7 +4245,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3096,9 +4261,9 @@ dependencies = [ [[package]] name = "tokio-postgres" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" dependencies = [ "async-trait", "byteorder", @@ -3107,13 +4272,13 @@ dependencies = [ "futures-channel", "futures-util", "log", - "parking_lot", + "parking_lot 0.12.3", "percent-encoding", "phf 0.11.3", "pin-project-lite", "postgres-protocol", "postgres-types", - "rand 0.8.5", + "rand 0.9.0", "socket2", "tokio", "tokio-util", @@ -3142,6 +4307,33 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-serde" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf600e7036b17782571dd44fa0a5cea3c82f60db5137f774a325a76a0d6852b" +dependencies = [ + "bincode", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", + "serde_json", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.13" @@ -3152,9 +4344,82 @@ dependencies = [ "futures-core", "futures-sink", "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.7.9", + "base64", + "bytes", + "h2 0.4.7", + "http 1.2.0", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", "tokio", + "tower-layer", + "tower-service", + "tracing", ] +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3181,7 +4446,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3205,6 +4470,23 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.27.0" +source = "git+https://github.com/zzlk/tracing-opentelemetry.git?branch=hack#c86d8b0b18195455fe7dd01374f7aafb18a4c52f" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + [[package]] name = "tracing-subscriber" version = "0.3.19" @@ -3281,6 +4563,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -3316,13 +4608,19 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" -version = "1.12.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.1", ] [[package]] @@ -3411,7 +4709,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -3446,7 +4744,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3514,9 +4812,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.7" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" dependencies = [ "rustls-pki-types", ] @@ -3527,7 +4825,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall", + "redox_syscall 0.5.8", "wasite", "web-sys", ] @@ -3563,6 +4861,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3762,7 +5069,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -3778,11 +5085,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.14" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" +checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" dependencies = [ - "zerocopy-derive 0.8.14", + "zerocopy-derive 0.8.17", ] [[package]] @@ -3793,18 +5100,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "zerocopy-derive" -version = "0.8.14" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" +checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3824,7 +5131,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -3853,7 +5160,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d88f740..6464d48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,10 @@ resolver = "2" members = ["crates/*"] default-members = ["crates/*"] +[patch.crates-io] +tracing-opentelemetry = { git = "https://github.com/zzlk/tracing-opentelemetry.git", branch = "hack", version = "0.27.0" } +# gsfs = { path = "../gsfs/rust/gsfs" } + # # Release # @@ -52,13 +56,13 @@ lto = "off" incremental = true codegen-units = 2048 -# [profile.dev.package."*"] -# opt-level = 2 -# debug = 2 -# debug-assertions = true -# overflow-checks = true -# incremental = true -# codegen-units = 2048 +[profile.dev.package."*"] +opt-level = 2 +debug = 2 +debug-assertions = true +overflow-checks = true +incremental = true +codegen-units = 2048 # [profile.dev.package.adler2] # opt-level = 2 diff --git a/Makefile b/Makefile index 4206ee3..d379367 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,37 @@ RUST_SOURCE = Cargo.toml Cargo.lock $(shell find crates/ -name "*.rs" -or -name "*.toml" | sed 's/ /\\ /g') RUST_TARGET_DIR := target/x86_64-unknown-linux-gnu -$(RUST_TARGET_DIR)/debug/scmscx-com: $(RUST_SOURCE) + +# FEATURES ?= "" +# /tmp/scmscx.com/FEATURES: .phony +# mkdir -p "$(@D)" +# @if [[ `cat ARGS 2>&1` != '$(FEATURES)' ]]; then echo -n $(FEATURES) >$@; fi +SHELL=/bin/bash + +ENABLE_GSFS ?= "" + +CONFIG ?= "" +/tmp/scmscx.com/ENABLE_GSFS: .phony + @mkdir -p "$(@D)" + @if [[ `cat $@ 2>&1` != '$(ENABLE_GSFS)' ]]; then echo -n "$(ENABLE_GSFS)" >$@; fi + +$(RUST_TARGET_DIR)/debug/scmscx-com: $(RUST_SOURCE) /tmp/scmscx.com/ENABLE_GSFS +ifeq ($(ENABLE_GSFS), 1) + cargo build --bin scmscx-com --config 'patch.crates-io.gsfs.git="ssh://git@github.com/zzlk/gsfs.git"' --config 'patch.crates-io.gsfs.branch="main"' --features "gsfs" +else ifeq ($(ENABLE_GSFS), 2) + cargo build --bin scmscx-com --config 'patch.crates-io.gsfs.path="../gsfs/rust/gsfs"' --features "gsfs" +else cargo build --bin scmscx-com -$(RUST_TARGET_DIR)/release/scmscx-com: $(RUST_SOURCE) +endif + +$(RUST_TARGET_DIR)/release/scmscx-com: $(RUST_SOURCE) /tmp/scmscx.com/ENABLE_GSFS +ifeq ($(shell cat /tmp/scmscx.com/ENABLE_GSFS), 1) + cargo build --release --bin scmscx-com --config 'patch.crates-io.gsfs.git="ssh://git@github.com/zzlk/gsfs.git"' --config 'patch.crates-io.gsfs.branch="main"' --features "gsfs" +else ifeq ($(shell cat /tmp/scmscx.com/ENABLE_GSFS), 2) + cargo build --release --bin scmscx-com --config 'patch.crates-io.gsfs.path="../gsfs/rust/gsfs"' --features "gsfs" +else cargo build --release --bin scmscx-com +endif package-lock.json node_modules &: package.json npm i @@ -85,4 +112,13 @@ deploy: ssh -i~/.ssh/stan -C stan@urmom.zxcv.io sudo systemctl restart \ container-scmscx.com-S1 -.PHONY: check build test fmt clippy ci run push dev deploy run-release +update: /tmp/scmscx.com/ENABLE_GSFS +ifeq ($(ENABLE_GSFS), 1) + cargo update --config 'patch.crates-io.gsfs.git="ssh://git@github.com/zzlk/gsfs.git"' --config 'patch.crates-io.gsfs.tag="scmscx.com"' +else ifeq ($(ENABLE_GSFS), 2) + cargo update --config 'patch.crates-io.gsfs.path="../gsfs/rust/gsfs"' +else + cargo update +endif + +.PHONY: .phony check build test fmt clippy ci run run-release push dev deploy update diff --git a/compose.yaml b/compose.yaml index 96ef5dd..844a46f 100644 --- a/compose.yaml +++ b/compose.yaml @@ -16,7 +16,7 @@ services: context: . environment: DEV_MODE: "true" - RUST_LOG: info + RUST_LOG: info,tarpc=warn DB_HOST: localhost DB_PORT: 5432 DB_USER: bounding.net @@ -24,3 +24,6 @@ services: DB_PASSWORD: anotverysecurepassword BACKBLAZE_DISABLED: "true" MIXPANEL_DISABLED: "true" + GSFS_USE_LOCAL: "false" + # JAEGER_ENDPOINT: http://127.0.0.1:4317 + JAEGER_ENDPOINT: http://10.70.18.1:4317 diff --git a/crates/bwcommon/src/logging.rs b/crates/bwcommon/src/logging.rs index cf5691a..b17995d 100644 --- a/crates/bwcommon/src/logging.rs +++ b/crates/bwcommon/src/logging.rs @@ -22,64 +22,66 @@ fn merge_json(a: &mut serde_json::Value, b: serde_json::Value) { } } -pub fn create_mixpanel_channel() -> std::sync::mpsc::Sender { +pub async fn create_mixpanel_channel() -> std::sync::mpsc::Sender { let (tx, rx) = std::sync::mpsc::channel::(); - std::thread::spawn(move || loop { - std::thread::sleep(std::time::Duration::from_secs(10)); - - let mut events = Vec::new(); - + tokio::spawn(async move { loop { - let result = rx.try_recv(); + tokio::time::sleep(std::time::Duration::from_secs(10)).await; + let mut events = Vec::new(); - if events.len() > 1900 { - break; - } + loop { + let result = rx.try_recv(); - match result { - Ok(v) => { - events.push(v); - } - Err(std::sync::mpsc::TryRecvError::Empty) => { + if events.len() > 1900 { break; } - Err(std::sync::mpsc::TryRecvError::Disconnected) => { - return; + + match result { + Ok(v) => { + events.push(v); + } + Err(std::sync::mpsc::TryRecvError::Empty) => { + break; + } + Err(std::sync::mpsc::TryRecvError::Disconnected) => { + return; + } } } - } - if events.len() > 0 { - if std::env::var("MIXPANEL_DISABLED").is_err() { - let ret = reqwest::blocking::Client::new() - .post("https://api.mixpanel.com/import") - .basic_auth( - std::env::var("MIXPANEL_ACCOUNT_NAME").unwrap(), - Some(std::env::var("MIXPANEL_API_KEY").unwrap()), - ) - .query(&[ - ("strict", 1), - ( - "project_id", - std::env::var("MIXPANEL_PROJECT_ID") - .unwrap() - .parse() - .unwrap(), - ), - ]) - .json(&events) - .send(); - - if let Err(err) = ret { - error!("error sending stuff to mixpanel: {err:?}"); - } else if let Ok(ret) = ret { - if ret.status() != 200 { - error!("error from mixpanel: {}", ret.text().unwrap()) + if events.len() > 0 { + if std::env::var("MIXPANEL_DISABLED").is_err() { + let ret = reqwest::Client::new() + .post("https://api.mixpanel.com/import") + .basic_auth( + std::env::var("MIXPANEL_ACCOUNT_NAME").unwrap(), + Some(std::env::var("MIXPANEL_API_KEY").unwrap()), + ) + .query(&[ + ("strict", 1), + ( + "project_id", + std::env::var("MIXPANEL_PROJECT_ID") + .unwrap() + .parse() + .unwrap(), + ), + ]) + .json(&events) + .send() + .await; + + if let Err(err) = ret { + error!("error sending stuff to mixpanel: {err:?}"); + } else if let Ok(ret) = ret { + if ret.status() != 200 { + error!("error from mixpanel: {}", ret.text().await.unwrap()) + } } - } - events.clear(); + events.clear(); + } } } }); diff --git a/crates/bwmapserver/Cargo.toml b/crates/bwmapserver/Cargo.toml index 8cb1376..a042bcb 100644 --- a/crates/bwmapserver/Cargo.toml +++ b/crates/bwmapserver/Cargo.toml @@ -3,7 +3,18 @@ name = "scmscx-com" version = "0.1.0" edition = "2021" +[features] +# default = ["gsfs"] +gsfs = [] + [dependencies] +gsfs = "*" +tracing-opentelemetry = "0.27.0" +opentelemetry = "0.26.0" +opentelemetry_sdk = { version = "0.26.0", features = ["rt-tokio"] } +opentelemetry-stdout = "0.26.0" +opentelemetry-otlp = "0.26.0" + # bwreplay = { path = "../bwreplay" } bwmap = { path = "../bwmap" } bwcommon = { path = "../bwcommon" } @@ -53,7 +64,7 @@ actix-files = "*" actix-multipart = "*" #env_logger = "*" #mime = "*" -time = "0.2.27" +# time = "0.2.27" reqwest = { version = "*", default-features = false, features = ["json", "stream", "http2", "h3", "rustls-tls"] } futures-core = "*" futures-util = "*" diff --git a/crates/bwmapserver/src/actix.rs b/crates/bwmapserver/src/actix.rs index f6a4c8b..6fcfc3e 100644 --- a/crates/bwmapserver/src/actix.rs +++ b/crates/bwmapserver/src/actix.rs @@ -3,13 +3,6 @@ use actix_web::HttpMessage; use actix_web::HttpRequest; use actix_web::{get, middleware, post, web, App, HttpResponse, Responder}; -//use anyhow::Context; - -//use crossbeam_channel::Sender; -//use rustls_pemfile::{certs}; - -//use image::DynamicImage; - use serde::{Deserialize, Serialize}; use crate::db; @@ -18,18 +11,17 @@ use crate::hacks; use bwcommon::insert_extension; use bwcommon::{ApiSpecificInfoForLogging, MyError}; -use anyhow::Result; -use backblaze::api::{b2_authorize_account, b2_download_file_by_name}; -use handlebars::{DirectorySourceOptions, Handlebars}; -// use r2d2_postgres::r2d2; use crate::util::is_dev_mode; use actix_files::Files; +use anyhow::Result; use async_stream::stream; use backblaze::api::b2_get_upload_url; use backblaze::api::b2_upload_file; use backblaze::api::B2AuthorizeAccount; +use backblaze::api::{b2_authorize_account, b2_download_file_by_name}; use bytes::BytesMut; use futures::lock::Mutex; +use handlebars::{DirectorySourceOptions, Handlebars}; use rand::Rng; use rand::SeedableRng; use reqwest::Client; @@ -99,6 +91,7 @@ async fn get_map( >, >, backblaze_auth: web::Data>, + #[cfg(feature = "gsfs")] _gsfs_client: web::Data, ) -> Result { let (mapblob_hash,) = path.into_inner(); @@ -134,6 +127,30 @@ async fn get_map( let mut retries_remaining = 5; let mut bad_version = None; + #[cfg(feature = "gsfs")] + { + // Don't serve maps from gsfs yet, it's unclear how reliable it is. + // // TODO: gsfs_client.get() here, if it works, return to browser, otherwise fallback to backblaze. + // let ret = gsfs_client + // .read_as_stream( + // format!("/scmscx.com/mapblob/{mapblob_hash}"), + // &[0; 32], + // 0..u64::MAX, + // ) + // .await; + + // match ret { + // Ok(stream) => { + // return Ok(insert_extension(HttpResponse::Ok(), info) + // .content_type("application/octet-stream") + // .streaming(futures::StreamExt::map(stream, |x| x.map(|x| x.freeze())))); + // } + // Err(e) => { + // error!("Failed to download from gsfs: {}", e); + // } + // } + } + while retries_remaining > 0 { let (version, api_info) = get_auth(&client, backblaze_auth.clone(), bad_version).await?; @@ -164,67 +181,6 @@ async fn get_map( return Ok(insert_extension(HttpResponse::InternalServerError(), info).finish()); } -// #[get("/api/replay_dump/{replay_id}")] -// async fn get_replay_dump( -// path: web::Path<(i64,)>, -// pool: web::Data< -// bb8_postgres::bb8::Pool< -// bb8_postgres::PostgresConnectionManager, -// >, -// >, -// ) -> Result { -// let (replay_id,) = path.into_inner(); -// let (uploaded_by, uploaded_time, replay_blob, denorm_scenario, chkhash, map_id) = { -// let r = pool.get().await?.query_one(" -// select account.username, replay.uploaded_time, replayblob.data, replay.denorm_scenario, replay.chkhash, map.id -// from replay -// join replayblob on replayblob.hash = replay.hash -// join account on account.id = uploaded_by -// full outer join map on map.chkblob = replay.chkhash -// where replay.id = $1", &[&replay_id]).await?; - -// ( -// r.try_get::<_, String>(0)?, -// r.try_get::<_, i64>(1)?, -// r.try_get::<_, Vec>(2)?, -// r.try_get::<_, Vec>(3)?, -// r.try_get::<_, String>(4)?, -// r.try_get::<_, i64>(5)?, -// ) -// }; - -// #[derive(Debug, Serialize, Deserialize)] -// struct ReplayInfo { -// uploaded_by: String, -// uploaded_time: i64, -// replay_header: bwreplay::ReplayHeader, -// denorm_scenario: String, -// chkhash: String, -// map_id: i64, -// } - -// let ret = ReplayInfo { -// uploaded_by, -// uploaded_time, -// replay_header: bwreplay::parse_replay_blob(replay_blob.as_slice())?.header, -// denorm_scenario: encoding_rs::UTF_8 -// .decode(denorm_scenario.as_slice()) -// .0 -// .to_string(), -// chkhash: chkhash, -// map_id: map_id, -// }; - -// let info = ApiSpecificInfoForLogging { -// replay_id: Some(replay_id), -// ..Default::default() -// }; - -// Ok(insert_extension(HttpResponse::Ok(), info) -// .content_type("application/json") -// .body(serde_json::to_string(&ret)?)) -// } - #[get("/api/replays/{replay_id}")] async fn get_replay( path: web::Path<(i64,)>, @@ -499,77 +455,6 @@ async fn get_minimap_resized( .insert_header(("Cache-Control", "public, max-age=60, immutable"))) } -// #[get("/api/random_bound")] -// async fn get_random_bound( -// pool: web::Data< -// r2d2::Pool>, -// >, -// ) -> Result { -// let map_id: i64 = { -// web::block(move || { -// let mut con = pool.get().unwrap(); -// con.query_one( -// " -// select mapid from ( -// select distinct map as mapid from stringmap2 -// where 'bound' <% data and ((scenario_name = true) or (scenario_description = true) or (force_names = true) or (file_names = true)) -// ) as sq1 -// join map on map.id = mapid -// where nsfw = false and broken = false and outdated = false and unfinished = false -// order by random() -// limit 1", -// &[], -// ) -// }) -// .await?? -// .get(0) -// }; - -// let info = ApiSpecificInfoForLogging { -// map_id: Some(map_id), -// ..Default::default() -// }; - -// Ok(insert_extension(HttpResponse::TemporaryRedirect(), info) -// .finish() -// .customize() -// .insert_header(("Location", format!("/map/{}", map_id)))) -// } - -// #[get("/api/random")] -// async fn get_random( -// pool: web::Data< -// r2d2::Pool>, -// >, -// ) -> Result { -// let map_id: i64 = { -// web::block(move || { -// let mut con = pool.get().unwrap(); - -// con.query_one( -// " -// select map.id from map -// where nsfw = false and outdated = false and unfinished = false and broken = false -// order by random() -// limit 1", -// &[], -// ) -// }) -// .await?? -// .get(0) -// }; - -// let info = ApiSpecificInfoForLogging { -// map_id: Some(map_id), -// ..Default::default() -// }; - -// Ok(insert_extension(HttpResponse::TemporaryRedirect(), info) -// .finish() -// .customize() -// .insert_header(("Location", format!("/map/{}", map_id)))) -// } - #[get("/api/get_selection_of_random_maps")] async fn get_selection_of_random_maps( req: HttpRequest, @@ -829,201 +714,6 @@ async fn add_tags( Ok(insert_extension(HttpResponse::Ok(), info).finish()) } -// #[post("/api/upload-replay")] -// async fn upload_replay( -// req: HttpRequest, -// mut payload: actix_multipart::Multipart, -// pool: web::Data< -// bb8_postgres::bb8::Pool< -// bb8_postgres::PostgresConnectionManager, -// >, -// >, -// ) -> Result { -// use std::io::Write; - -// let user_id = if let Some(user_id) = bwcommon::check_auth4(&req, (**pool).clone()).await? { -// user_id -// } else { -// return Ok(HttpResponse::Unauthorized().finish().customize()); -// }; - -// let mut dirpath; -// let mut filepath = "/xxx".to_string(); -// // iterate over multipart stream -// while let Ok(Some(mut field)) = payload.try_next().await { -// let content_type = field -// .content_disposition() -// .ok_or(anyhow::anyhow!("no content disposition"))?; -// let filename = content_type.get_filename().unwrap(); -// dirpath = format!( -// "/tmp/replays/{}", -// uuid::Uuid::new_v4().as_simple().to_string() -// ); -// filepath = format!("{}/{}", dirpath, sanitize_filename::sanitize(&filename)); - -// // File::create is blocking operation, use threadpool -// web::block(|| std::fs::create_dir_all(dirpath)).await??; - -// let c = filepath.clone(); -// // File::create is blocking operation, use threadpool -// let mut f = web::block(|| std::fs::File::create(c)).await??; - -// // Field in turn is stream of *Bytes* object -// while let Some(chunk) = field.next().await { -// let data = chunk.unwrap(); -// // filesystem operations are blocking, we have to use threadpool -// f = web::block(move || f.write_all(&data).map(|_| f)).await??; -// } -// } - -// let replay_blob = web::block(move || std::fs::read(filepath)).await??; - -// let replay_id = db::insert_replay(replay_blob.as_slice(), user_id, pool).await?; - -// let info = ApiSpecificInfoForLogging { -// user_id: Some(user_id), -// replay_id: Some(replay_id), -// ..Default::default() -// }; - -// Ok(insert_extension(HttpResponse::SeeOther(), info) -// .finish() -// .customize() -// .insert_header(("location", format!("/replay/{}", replay_id)))) -// } - -// #[derive(serde::Deserialize)] -// struct ResetPasswordFormData { -// password: String, -// password_confirm: String, -// } - -// #[post("/api/reset-password")] -// async fn reset_password( -// req: HttpRequest, -// form: web::Form, -// pool: web::Data< -// r2d2::Pool>, -// >, -// ) -> Result { -// let user_id = if let Some(user_id) = bwcommon::check_auth2(&req, pool.clone()).await { -// user_id -// } else { -// return Ok(HttpResponse::Unauthorized().finish()); -// }; - -// if form.password.len() == 0 { -// return Ok(HttpResponse::Conflict().body("passwords needs to be at least 1 character long")); -// } - -// if form.password != form.password_confirm { -// return Ok(HttpResponse::Conflict().body("passwords did not match")); -// } - -// db::change_password(user_id, form.password.clone(), (**pool).clone()).await?; - -// let info = ApiSpecificInfoForLogging { -// user_id: Some(user_id), -// ..Default::default() -// }; - -// Ok(insert_extension(HttpResponse::SeeOther(), info) -// .header("location", "/") -// .finish()) -// } - -// #[post("/api/register")] -// async fn register( -// form: web::Form, -// pool: web::Data< -// r2d2::Pool>, -// >, -// ) -> Result { -// if let Ok(token) = db::register( -// form.username.clone(), -// form.password.clone(), -// (**pool).clone(), -// ) -// .await -// { -// let info = ApiSpecificInfoForLogging { -// username: Some(form.username.clone()), -// ..Default::default() -// }; - -// Ok(insert_extension(HttpResponse::SeeOther(), info) -// .header("location", "/") -// .cookie( -// actix_http::http::Cookie::build("token", token) -// .path("/") -// .same_site(actix_http::cookie::SameSite::Lax) -// .secure(true) -// .permanent() -// .finish(), -// ) -// .cookie( -// actix_http::http::Cookie::build("username", &form.username) -// .path("/") -// .same_site(actix_http::cookie::SameSite::Lax) -// .secure(true) -// .permanent() -// .finish(), -// ) -// .finish()) -// } else { -// Ok(HttpResponse::Unauthorized().finish()) -// } -// } - -// #[derive(serde::Deserialize)] -// struct FormData { -// username: String, -// password: String, -// } - -// #[post("/api/login")] -// async fn login( -// form: web::Form, -// pool: web::Data< -// r2d2::Pool>, -// >, -// ) -> Result { -// if let Ok(token) = db::login( -// form.username.clone(), -// form.password.clone(), -// (**pool).clone(), -// ) -// .await -// { -// let info = ApiSpecificInfoForLogging { -// username: Some(form.username.clone()), -// ..Default::default() -// }; - -// Ok(insert_extension(HttpResponse::SeeOther(), info) -// .header("location", "/") -// .cookie( -// actix_http::http::Cookie::build("token", token) -// .path("/") -// .same_site(actix_http::cookie::SameSite::Lax) -// .secure(true) -// .permanent() -// .finish(), -// ) -// .cookie( -// actix_http::http::Cookie::build("username", &form.username) -// .path("/") -// .same_site(actix_http::cookie::SameSite::Lax) -// .secure(true) -// .permanent() -// .finish(), -// ) -// .finish()) -// } else { -// Ok(HttpResponse::Unauthorized().finish()) -// } -// } - fn parse_lst_files() -> std::collections::HashMap> { fn parse_lst_file(path: &std::path::Path) -> std::collections::HashMap { use std::io::prelude::*; @@ -1077,7 +767,7 @@ fn parse_lst_files() -> std::collections::HashMap Result< +async fn setup_db() -> Result< bb8_postgres::bb8::Pool< bb8_postgres::PostgresConnectionManager, >, @@ -1115,70 +805,235 @@ async fn setup_db_new() -> Result< anyhow::Ok(pool) } -pub(crate) async fn start() -> Result<()> { - //env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); +async fn start_file_pumper(#[cfg(feature = "gsfs")] gsfs_client: gsfs::Client) -> Result<()> { + let client = ClientBuilder::new().https_only(true).build()?; + + if let Err(e) = tokio::fs::create_dir_all("./pending").await { + error!("failed to create pending directory: {e}"); + } + + tokio::task::spawn(async move { + 'full_retry: loop { + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + + let upload_url = match std::env::var("BACKBLAZE_DISABLED") { + Ok(v) if v != "true" => { + let api_info = match b2_authorize_account( + &client, + &std::env::var("BACKBLAZE_KEY_ID").unwrap(), + &std::env::var("BACKBLAZE_APPLICATION_KEY").unwrap(), + ) + .await + { + Ok(v) => v, + Err(e) => { + error!("Failed to authorize account: {e}"); + continue; + } + }; - let db_new = setup_db_new().await.unwrap(); + Some( + match b2_get_upload_url( + &client, + &api_info, + &std::env::var("BACKBLAZE_MAPBLOB_BUCKET").unwrap(), + ) + .await + { + Ok(upload_info) => upload_info, + Err(e) => { + error!("Failed to get upload url, trying again: {e}"); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + continue; + } + }, + ) + } + _ => None, + }; - let handlebars = { - let mut registry = Handlebars::new(); + loop { + tokio::time::sleep(std::time::Duration::from_secs(5)).await; - registry.set_strict_mode(true); + let mut entries = match tokio::fs::read_dir("./pending").await { + Ok(v) => v, + Err(e) => { + error!("could not readdir: {e:?}"); + continue; + } + }; - if is_dev_mode() { - info!("DEV_MODE activated, template hot reloading"); - registry.set_dev_mode(true); + while let Ok(Some(entry)) = entries.next_entry().await { + let Ok(filename) = entry.file_name().into_string() else { + error!("could not stringify filename: {:?}", entry.file_name()); + continue; + }; + + info!("attempting to upload file: {filename}"); + + let mut split = filename.split('-'); + let Some(sha1) = split.next() else { + error!("could not extract sha1 part: {:?}", filename); + continue; + }; + let Some(sha256) = split.next() else { + error!("could not extract sha256 part: {:?}", filename); + continue; + }; + + #[cfg(feature = "gsfs")] + { + match gsfs::read_path_as_stream(entry.path(), 1024 * 1024).await { + Ok(stream) => { + let key = format!("/scmscx.com/mapblob/{sha256}"); + + match gsfs_client + .put( + &key, + &[0; 32], + stream, + gsfs::PutOptions { + ..Default::default() + }, + ) + .await + { + Ok(object_id) => { + info!( + "successfully uploaded to gsfs. ObjectID: {object_id}, key: {key}" + ); + } + Err(e) => { + error!("failed to gsfs put: {e}"); + } + } + } + Err(e) => { + error!("failed to read gsfs file: {e}"); + } + } + } + + let mut file = match tokio::fs::File::open(entry.path()).await { + Ok(v) => v, + Err(e) => { + error!("failed to open file: {e:?}"); + continue; + } + }; + + let metadata = match file.metadata().await { + Ok(v) => v, + Err(e) => { + error!("failed to get file metadata: {e:?}"); + continue; + } + }; + + let sm = stream! { + loop { + let mut bytes = BytesMut::with_capacity(8 * 1024 * 1024); + let bytes_read = file.read_buf(&mut bytes).await?; + if bytes_read == 0 { + break; + } + + yield anyhow::Ok(bytes); + } + }; + + if let Some(upload_info) = &upload_url { + match b2_upload_file( + &client, + &upload_info, + sha256, + metadata.len() as usize, + sha1.to_owned(), + sm, + ) + .await + { + Ok(_) => {} + Err(e) => { + error!("failed to b2_upload_file: {e}"); + continue 'full_retry; + } + } + } + + info!("Finished uploading file. sha256: {sha256}, sha1: {sha1}"); + + match tokio::fs::remove_file(entry.path()).await { + Ok(_) => {} + Err(e) => { + error!("failed to remove file: {e}"); + continue; + } + } + } + } } + }); - let mut options = DirectorySourceOptions::default(); - options.tpl_extension = ".hbs".to_owned(); - registry - .register_templates_directory( - std::path::Path::new(std::env::var("ROOT_DIR").unwrap().as_str()).join("uiv2"), - options, - ) - .unwrap(); + Ok(()) +} - registry - .register_partial( - "header.hbs", - std::fs::read_to_string( - std::path::Path::new(std::env::var("ROOT_DIR").unwrap().as_str()) - .join("uiv2/header.hbs"), - )? - .as_str(), - ) - .map_err(|err| anyhow::anyhow!("failed to unwrap. err: {:?}", err))?; - - registry - .register_partial( - "body.hbs", - std::fs::read_to_string( - std::path::Path::new(std::env::var("ROOT_DIR").unwrap().as_str()) - .join("uiv2/body.hbs"), - )? - .as_str(), - ) - .map_err(|err| anyhow::anyhow!("failed to unwrap. err: {:?}", err))?; +fn register_handlebars() -> Result>> { + let mut registry = Handlebars::new(); - web::Data::new(registry) - }; + registry.set_strict_mode(true); - //let manager = r2d2_sqlite::SqliteConnectionManager::file(std::env::var("DB_PATH").unwrap().as_str()); + if is_dev_mode() { + info!("DEV_MODE activated, template hot reloading"); + registry.set_dev_mode(true); + } - let tx = bwcommon::create_mixpanel_channel(); + let mut options = DirectorySourceOptions::default(); + options.tpl_extension = ".hbs".to_owned(); + registry + .register_templates_directory( + std::path::Path::new(std::env::var("ROOT_DIR").unwrap().as_str()).join("uiv2"), + options, + ) + .unwrap(); + + registry + .register_partial( + "header.hbs", + std::fs::read_to_string( + std::path::Path::new(std::env::var("ROOT_DIR").unwrap().as_str()) + .join("uiv2/header.hbs"), + )? + .as_str(), + ) + .map_err(|err| anyhow::anyhow!("failed to unwrap. err: {:?}", err))?; + + registry + .register_partial( + "body.hbs", + std::fs::read_to_string( + std::path::Path::new(std::env::var("ROOT_DIR").unwrap().as_str()) + .join("uiv2/body.hbs"), + )? + .as_str(), + ) + .map_err(|err| anyhow::anyhow!("failed to unwrap. err: {:?}", err))?; - let cache_droper = web::Data::new(std::sync::Mutex::new( - std::collections::HashSet::::new(), - )); + Ok(web::Data::new(registry)) +} - let db_clone = db_new.clone(); +fn start_materialized_view_refresher( + pool: &bb8_postgres::bb8::Pool< + bb8_postgres::PostgresConnectionManager, + >, +) -> Result<()> { let mut rng = rand::rngs::SmallRng::from_rng(&mut rand::rng()); + let pool = pool.clone(); tokio::spawn(async move { loop { info!("Refreshing materialized view"); - match db_clone.get().await { + match pool.get().await { Ok(con) => { match con .execute("REFRESH MATERIALIZED VIEW CONCURRENTLY user_stats", &[]) @@ -1204,152 +1059,16 @@ pub(crate) async fn start() -> Result<()> { } }); - // let set = web::Data::new(Mutex::new(SearchDatabase::new())); - - // { - // let db_new = db_new.clone(); - // let set = set.clone(); - // tokio::spawn(async move { - // populate_search_database(set, db_new).await.unwrap(); - // }); - // } - - // { - // let mut con = db_new.get().await?; - // let mut tx = con.transaction().await?; - // bwcommon::denormalize_map_tx(12325, &mut tx).await?; - // tx.commit().await?; - // } - - // Pump files up to backblaze - { - let client = ClientBuilder::new().https_only(true).build()?; - - if let Err(e) = tokio::fs::create_dir_all("./pending").await { - error!("failed to create pending directory: {e}"); - } - - if std::env::var("BACKBLAZE_DISABLED").is_err() { - tokio::task::spawn(async move { - 'full_retry: loop { - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - - let api_info = match b2_authorize_account( - &client, - &std::env::var("BACKBLAZE_KEY_ID").unwrap(), - &std::env::var("BACKBLAZE_APPLICATION_KEY").unwrap(), - ) - .await - { - Ok(v) => v, - Err(e) => { - error!("Failed to authorize account: {e}"); - continue; - } - }; - - const MAPBLOB_BUCKET: &'static str = "784baffe8e56dc107ee50d1c"; - // const TEST_BUCKET_2: &'static str = "082baf7e0e563c508ef50d1c"; - - let upload_info = - match b2_get_upload_url(&client, &api_info, MAPBLOB_BUCKET).await { - Ok(upload_info) => upload_info, - Err(e) => { - error!("Failed to get upload url, trying again: {e}"); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - continue; - } - }; - - loop { - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - - let mut entries = match tokio::fs::read_dir("./pending").await { - Ok(v) => v, - Err(e) => { - error!("could not readdir: {e:?}"); - continue; - } - }; - - while let Ok(Some(entry)) = entries.next_entry().await { - let Ok(filename) = entry.file_name().into_string() else { - error!("could not stringify filename: {:?}", entry.file_name()); - continue; - }; - - info!("attempting to upload file: {filename}"); - - let mut split = filename.split('-'); - let Some(sha1) = split.next() else { - error!("could not extract sha1 part: {:?}", filename); - continue; - }; - let Some(sha256) = split.next() else { - error!("could not extract sha256 part: {:?}", filename); - continue; - }; - - let mut file = match tokio::fs::File::open(entry.path()).await { - Ok(v) => v, - Err(e) => { - error!("failed to open file: {e:?}"); - continue; - } - }; - - let metadata = match file.metadata().await { - Ok(v) => v, - Err(e) => { - error!("failed to get file metadata: {e:?}"); - continue; - } - }; - - let sm = stream! { - loop { - let mut bytes = BytesMut::with_capacity(8 * 1024 * 1024); - let bytes_read = file.read_buf(&mut bytes).await?; - if bytes_read == 0 { - break; - } + Ok(()) +} - yield anyhow::Ok(bytes); - } - }; - - match b2_upload_file( - &client, - &upload_info, - sha256, - metadata.len() as usize, - sha1.to_owned(), - sm, - ) - .await - { - Ok(_) => {} - Err(e) => { - error!("failed to b2_upload_file: {e}"); - continue 'full_retry; - } - } +pub(crate) async fn start() -> Result<()> { + let db_pool = setup_db().await?; + start_materialized_view_refresher(&db_pool)?; - info!("Finished uploading file. sha256: {sha256}, sha1: {sha1}"); + let handlebars = register_handlebars()?; - match tokio::fs::remove_file(entry.path()).await { - Ok(_) => {} - Err(e) => { - error!("failed to remove file: {e}"); - continue; - } - } - } - } - } - }); - } - } + let tx = bwcommon::create_mixpanel_channel().await; let manifest = { web::Data::new(serde_json::from_str::>( @@ -1359,16 +1078,35 @@ pub(crate) async fn start() -> Result<()> { )?) }; + #[cfg(feature = "gsfs")] + let gsfs_client = { + match std::env::var("GSFS_USE_LOCAL") { + Ok(v) if v == "true" => web::Data::new(gsfs::Client::with_local_config().await?), + _ => web::Data::new(gsfs::Client::with_ppe_config().await?), + } + }; + + // Pump files up to backblaze + start_file_pumper( + #[cfg(feature = "gsfs")] + (**gsfs_client).clone(), + ) + .await?; + let server = actix_web::HttpServer::new(move || { let svc = App::new() - .app_data(web::Data::new(cache_droper.clone())) .app_data(web::Data::new(tx.clone())) - .app_data(web::Data::new(db_new.clone())) + .app_data(web::Data::new(db_pool.clone())) .app_data(web::Data::new(Mutex::new(BackblazeAuth::default()))) .app_data(handlebars.clone()) .app_data(parse_lst_files()) .app_data(manifest.clone()) - .app_data(web::Data::new(awc::Client::default())) + .app_data(web::Data::new(awc::Client::default())); + + #[cfg(feature = "gsfs")] + let svc = svc.app_data(gsfs_client.clone()); + + let svc = svc // .app_data(set.clone()) .wrap(middleware::Compress::default()) .wrap(middleware::NormalizePath::trim()) @@ -1412,7 +1150,6 @@ pub(crate) async fn start() -> Result<()> { // API .service(crate::api::flags::get_flag) .service(crate::api::flags::set_flag) - .service(crate::api::bulkupload::post_handler) .service(crate::api::change_password::post_handler) .service(crate::api::change_username::post_handler) .service(crate::api::login::post_handler) @@ -1469,26 +1206,8 @@ pub(crate) async fn start() -> Result<()> { .service(hacks::denormalize) .service(hacks::denormalize_all) // Static pages - // .service(crate::static_pages::author) - // .service(crate::static_pages::viewer) - // .service(crate::static_pages::viewer2) - // .service(crate::static_pages::css) - // .service(crate::static_pages::get_reset_password_static_page) - // .service(crate::static_pages::ladder_static_page) - // .service(crate::static_pages::recent_static_page) - // .service(crate::static_pages::get_replays_static_page) - // .service(crate::static_pages::get_replay_static_page) - // .service(crate::static_pages::robots) - // .service(crate::static_pages::get_login_page) - // .service(crate::static_pages::get_register_page) - // .service(crate::static_pages::get_upload_replay_page) - // .service(crate::static_pages::favicon) - // .service(crate::static_pages::libjs) .service(crate::static_pages::redirect_map) .service(crate::static_pages::redirect_replay) - // .service(crate::static_pages::grp) - // .service(crate::static_pages::bwrender_wasm_js) - // .service(crate::static_pages::bwrender_wasm_bg_wasm) .service( Files::new("/assets", "./dist/assets/") .use_etag(false) diff --git a/crates/bwmapserver/src/api/bulkupload.rs b/crates/bwmapserver/src/api/bulkupload.rs index f8c8e73..1f6af59 100644 --- a/crates/bwmapserver/src/api/bulkupload.rs +++ b/crates/bwmapserver/src/api/bulkupload.rs @@ -1,277 +1,12 @@ -use crate::actix::BackblazeAuth; -use crate::middleware::UserSession; -use actix_multipart::Field; -use actix_web::{post, web, HttpMessage, HttpRequest}; -use actix_web::{HttpResponse, Responder}; -use anyhow::anyhow; use anyhow::Result; -use async_stream::stream; -use backblaze::api::{b2_get_upload_url, b2_upload_file, B2GetUploadUrl}; use bb8_postgres::tokio_postgres::IsolationLevel; use bwmap::ParsedChk; -use bytes::BytesMut; -use futures::lock::Mutex; -use futures_util::StreamExt; -use futures_util::TryStreamExt; use rand::Rng; -use reqwest::Client; -use serde::Serialize; -use sha1::{Digest, Sha1}; -use sha2::Sha256; -use std::collections::HashMap; +use sha1::Digest; use std::fmt::Debug; use std::time::Duration; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tracing::error; -#[post("/api/upload-maps/{playlist}")] -pub(crate) async fn post_handler( - req: HttpRequest, - path: web::Path<(String,)>, - mut payload: actix_multipart::Multipart, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - backblaze_auth: web::Data>, -) -> Result { - let user_id = req - .extensions() - .get::() - .map(|x| x.id) - .unwrap_or(10); - - let (playlist,) = path.into_inner(); - - let playlist_id: i64 = { - let con = pool.get().await?; - - if let Some(row) = con - .query( - "select id from playlist where name = $1 and owner = $2", - &[&playlist, &user_id], - ) - .await? - .pop() - { - row.get("id") - } else { - con.query_one( - "insert into playlist (owner, name) values ($1, $2) returning id", - &[&user_id, &playlist], - ) - .await? - .get("id") - } - }; - - let client = Client::new(); - - // const ARCHIVE_BUCKET: &'static str = "b80b4f2e8e26dc107ee50d1c"; - // const UPLOAD_BUCKET: &'static str = "38cb8fae7e062c108e550d1c"; - const MAPBLOB_BUCKET: &'static str = "784baffe8e56dc107ee50d1c"; - - let (_, api_info) = crate::actix::get_auth(&client, backblaze_auth.clone(), None).await?; - - let upload_info = b2_get_upload_url(&client, &api_info, MAPBLOB_BUCKET) - .await - .map_err(|e| anyhow::anyhow!(e))?; - - let mut ret = Vec::new(); - - #[derive(Debug, Serialize)] - enum Ret { - Link(String), - Err(String), - } - - while let Ok(Some(field)) = payload.try_next().await { - ret.push( - match process_field( - &client, - &upload_info, - user_id, - playlist_id, - (**pool).clone(), - field, - ) - .await - { - Ok(link) => Ret::Link(link), - Err(err) => Ret::Err(err.to_string()), - }, - ); - } - - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(serde_json::to_string(&ret)?) - .customize()) -} - -async fn process_field( - client: &Client, - upload_info: &B2GetUploadUrl, - user_id: i64, - playlist_id: i64, - pool: bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - mut field: Field, -) -> Result { - let content_disposition = field - .content_disposition() - .ok_or(anyhow!("content_disposition not found"))?; - - match content_disposition.disposition { - actix_web::http::header::DispositionType::FormData => {} - _ => anyhow::bail!( - "content disposition is invalid: content_disposition{:?}", - content_disposition - ), - } - - let mut last_modified_time = Err(anyhow!("last_modified_time not found")); - let mut file_size = Err(anyhow!("file_size not found")); - let mut file_name = Err(anyhow!("file_name not found")); - let mut file_sha256hash = Err(anyhow!("file_sha256hash not found")); - let mut file_sha1hash = Err(anyhow!("file_sha1hash not found")); - - for param in &content_disposition.parameters { - match param { - actix_web::http::header::DispositionParam::Name(s) => { - let split: Vec<&str> = s.split("/").collect(); - if split.len() != 4 { - continue; - } - - if let Ok(x) = split[0].parse::() { - last_modified_time = Ok(x / 1000); - } - - if let Ok(x) = split[1].parse::() { - file_size = Ok(x); - } - - file_sha256hash = Ok(split[2].to_owned()); - file_sha1hash = Ok(split[3].to_owned()); - } - actix_web::http::header::DispositionParam::Filename(s) => { - let split = s.split("/"); - file_name = Ok(split.last().unwrap().to_owned()); - } - actix_web::http::header::DispositionParam::FilenameExt(_) => {} - actix_web::http::header::DispositionParam::Unknown(_, _) => {} - actix_web::http::header::DispositionParam::UnknownExt(_, _) => {} - } - } - - let last_modified_time = last_modified_time?; - let file_size = file_size?; - let file_name = file_name?; - let file_sha256hash = file_sha256hash?; - let file_sha1hash = file_sha1hash?; - - let fake_filename = format!("/tmp/{}", uuid::Uuid::new_v4().as_simple()); - - let mut sha256hasher = Sha256::new(); - let mut sha1hasher = Sha1::new(); - - { - let mut file = tokio::fs::File::create(fake_filename.as_str()).await?; - let mut total_file_size = 0; - - while let Some(chunk) = field.next().await { - match chunk { - Ok(bytes) => { - total_file_size += bytes.len(); - if total_file_size > file_size { - anyhow::bail!("file too big. total_file_size: {total_file_size}, file_size: {total_file_size}"); - } - - sha1hasher.update(&bytes[..]); - sha256hasher.update(&bytes[..]); - file.write_all(&bytes[..]).await?; - } - Err(err) => anyhow::bail!("err: {:?}", err), - } - } - - if total_file_size != file_size { - anyhow::bail!("incorrect file size. total_file_size: {total_file_size}, file_size: {total_file_size}"); - } - - file.sync_all().await?; - file.flush().await?; - } - - let sha256hash = format!("{:x}", sha256hasher.finalize()); - let sha1hash = format!("{:x}", sha1hasher.finalize()); - - anyhow::ensure!(sha256hash == *file_sha256hash); - anyhow::ensure!(sha1hash == *file_sha1hash); - - let sm = { - let fake_filename = fake_filename.clone(); - - stream! { - let mut file = tokio::fs::File::open(fake_filename.as_str()).await?; - - loop { - let mut buf = BytesMut::with_capacity(1024*1024); - let bytes_read = file.read_buf(&mut buf).await?; - if bytes_read == 0 { - break; - } - - yield anyhow::Ok(buf); - } - } - }; - - b2_upload_file( - &client, - &upload_info, - sha256hash.as_str(), - file_size, - sha1hash.clone(), - sm, - ) - .await?; - - let mut new_tags = HashMap::new(); - new_tags.insert("autogen_bulk_uploaded2".to_owned(), "true".to_owned()); - - let map_id = insert_map( - file_name.as_str(), - fake_filename.as_str(), - sha256hash.as_str(), - file_size, - user_id, - playlist_id, - new_tags, - pool, - Some(last_modified_time), - ) - .await?; - - tokio::fs::remove_file(fake_filename).await?; - - let map_id = bwcommon::get_web_id_from_db_id(map_id, crate::util::SEED_MAP_ID)?; - - anyhow::Ok(format!("https://scmscx.com/map/{}", map_id)) -} - -// seventyseven-uploadCreated:December 17, 2022 -// Bucket ID:38cb8fae7e062c108e550d1c -// Type:Private -// File Lifecycle:Keep all versions -// Snapshots:0 -// Current Files:0 -// Current Size:0 bytes -// Endpoint:s3.us-west-004.backblazeb2.com -// Encryption:Disabled #[allow(clippy::too_many_arguments)] pub(crate) async fn insert_map( filename: &str, diff --git a/crates/bwmapserver/src/api/uiv2/upload.rs b/crates/bwmapserver/src/api/uiv2/upload.rs index b229a92..2c07e15 100644 --- a/crates/bwmapserver/src/api/uiv2/upload.rs +++ b/crates/bwmapserver/src/api/uiv2/upload.rs @@ -147,56 +147,3 @@ async fn upload_map( .body(serde_json::to_string(&json!(map_id))?) .customize()) } - -// let (tx, mut rx) = tokio::sync::mpsc::channel::(1); - -// let join_handle = { -// let client = Client::new(); -// //const MAPBLOB_BUCKET: &'static str = "784baffe8e56dc107ee50d1c"; -// const TEST_BUCKET_2: &'static str = "082baf7e0e563c508ef50d1c"; - -// let mut retries_left = 5; -// let mut bad_version = None; - -// let upload_info = loop { -// retries_left -= 1; - -// let (version, api_info) = -// crate::actix::get_auth(&client, backblaze_auth.clone(), bad_version).await?; - -// let upload_info = match b2_get_upload_url(&client, &api_info, TEST_BUCKET_2).await { -// Ok(upload_info) => upload_info, -// Err(e) => { -// error!("Failed to get upload url, trying again: {e}"); -// bad_version = Some(version); -// tokio::time::sleep(std::time::Duration::from_millis(500)).await; -// continue; -// } -// }; - -// break upload_info; -// }; - -// let sha256hash = query.sha256.clone(); -// let sha1hash = query.sha1.clone(); -// let total_file_size = query.length; - -// tokio::task::spawn(async move { -// let sm = stream! { -// while let Some(bytes) = rx.recv().await { -// error!("yielding bytes: {}", bytes.len()); -// yield anyhow::Ok(bytes); -// } -// }; - -// b2_upload_file( -// &client, -// &upload_info, -// sha256hash.as_str(), -// total_file_size, -// sha1hash.clone(), -// sm, -// ) -// .await -// }) -// }; diff --git a/crates/bwmapserver/src/main.rs b/crates/bwmapserver/src/main.rs index 544aa08..438fe47 100644 --- a/crates/bwmapserver/src/main.rs +++ b/crates/bwmapserver/src/main.rs @@ -1,58 +1,72 @@ -use tracing_log::LogTracer; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; - mod actix; mod api; mod db; mod hacks; mod middleware; -// mod search; mod search2; -// mod ssr; mod static_pages; mod tests; mod uiv2; mod util; -// #[actix_web::main] +use opentelemetry::trace::TracerProvider; +use opentelemetry_otlp::WithExportConfig; +use tracing_log::LogTracer; +use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, EnvFilter, Layer}; #[tokio::main] async fn main() -> anyhow::Result<()> { - // enable console_subcriber only in debug build because it consumes so much memory it breaks the server - if cfg!(debug_assertions) { - //console_subscriber::init(); - } - LogTracer::init().expect("Failed to set logger"); - // let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); - // let formatting_layer = BunyanFormattingLayer::new( - // "zero2prod".into(), - // // Output the formatted spans to stdout. - // std::io::stdout, + let mut builder = opentelemetry_sdk::trace::TracerProvider::builder(); + + if let Ok(endpoint) = &std::env::var("JAEGER_ENDPOINT") { + builder = builder.with_batch_exporter( + // opentelemetry_otlp::SpanExporter::builder() + // .with_tonic() + // .with_endpoint(endpoint) + // .build()?, + opentelemetry_otlp::new_exporter() + .tonic() + .with_endpoint(endpoint) + .build_span_exporter()?, + opentelemetry_sdk::runtime::Tokio, + ); + } + + // builder = builder.with_batch_exporter( + // opentelemetry_stdout::SpanExporter::default(), + // opentelemetry_sdk::runtime::Tokio, // ); - // The `with` method is provided by `SubscriberExt`, an extension - // trait for `Subscriber` exposed by `tracing_subscriber` - // let subscriber = Registry::default() - // .with(env_filter) - // .with(JsonStorageLayer) - // .with(formatting_layer); - // // `set_global_default` can be used by applications to specify - // // what subscriber should be used to process spans. - // set_global_default(subscriber).expect("Failed to set subscriber"); - let filter = EnvFilter::from_default_env(); - let subscriber = tracing_subscriber::fmt() - // filter spans/events with level TRACE or higher. - .with_env_filter(filter) - .with_span_events(FmtSpan::CLOSE) - .with_file(true) - .with_target(false) - .with_line_number(true) - // build but do not install the subscriber. - .finish(); + builder = builder.with_config(opentelemetry_sdk::trace::Config::default().with_resource( + opentelemetry_sdk::Resource::new([ + opentelemetry::KeyValue::new("service.name", "scmscx.com"), + opentelemetry::KeyValue::new("node", "scmscx.com"), + ]), + )); + + let tracer_provider = builder.build(); + let tracer = tracer_provider.tracer("scmscx.com"); - tracing::subscriber::set_global_default(subscriber)?; + tracing::subscriber::set_global_default( + tracing_subscriber::registry() + .with( + tracing_opentelemetry::layer() + .with_tracer(tracer) + .with_filter(tracing_subscriber::EnvFilter::new( + "trace,h2=info,scmscx_com=off,bwmap=off,bwmpq=off", + )), + ) + .with( + tracing_subscriber::fmt::layer() + .with_span_events(FmtSpan::CLOSE) + .with_file(true) + .with_target(false) + .with_line_number(true) + .with_filter(EnvFilter::from_default_env()), + ), + )?; //tracing_subscriber::fmt::init(); diff --git a/crates/bwmapserver/src/middleware/traceid.rs b/crates/bwmapserver/src/middleware/traceid.rs index f13d52b..f60cd75 100644 --- a/crates/bwmapserver/src/middleware/traceid.rs +++ b/crates/bwmapserver/src/middleware/traceid.rs @@ -51,25 +51,29 @@ where forward_ready!(service); - #[instrument(skip_all, name = "", fields(trace_id))] + #[instrument(skip_all, name = "traceid-middleware", fields(trace_id))] fn call(&self, req: ServiceRequest) -> Self::Future { let path = req.path().to_owned(); + let trace_id: String = uuid::Uuid::new_v4() .as_simple() .to_string() .chars() .take(6) .collect(); + let ip = req .connection_info() .realip_remote_addr() .unwrap_or("x.x.x.x") .to_owned(); + let user_agent = req .headers() .get("user-agent") .map(|x| x.to_str().unwrap_or("couldn't unwrap").to_owned()) .unwrap_or("couldn't unwrap2".to_string()); + req.extensions_mut().insert(TraceID { id: trace_id.clone(), start_time: Instant::now(), diff --git a/crates/bwmapserver/src/ssr/about.rs b/crates/bwmapserver/src/ssr/about.rs deleted file mode 100644 index a0c8672..0000000 --- a/crates/bwmapserver/src/ssr/about.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/about")] -async fn handler( - req: HttpRequest, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - let map_count = async { - let con = pool.get().await?; - - anyhow::Ok( - con.query_one("select count(*) from map", &[]) - .await? - .try_get::<_, i64>(0)?, - ) - }; - - let map_views_last_1_day = async { - let con = pool.get().await?; - - anyhow::Ok( - con.query_one("select views from user_stats where days = 1", &[]) - .await? - .try_get::<_, i64>(0)?, - ) - }; - - let map_views_last_7_days = async { - let con = pool.get().await?; - - anyhow::Ok( - con.query_one("select views from user_stats where days = 7", &[]) - .await? - .try_get::<_, i64>(0)?, - ) - }; - - let map_views_last_30_days = async { - let con = pool.get().await?; - - anyhow::Ok( - con.query_one("select views from user_stats where days = 30", &[]) - .await? - .try_get::<_, i64>(0)?, - ) - }; - - let (map_count, map_views_last_1_day, map_views_last_7_days, map_views_last_30_days) = futures_util::try_join!( - map_count, - map_views_last_1_day, - map_views_last_7_days, - map_views_last_30_days - )?; - - let new_html = hb.render( - "about", - &json!({ - "map_count": map_count, - "map_views_last_1_day": map_views_last_1_day, - "map_views_last_7_days": map_views_last_7_days, - "map_views_last_30_days": map_views_last_30_days, - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?; - - Ok(HttpResponse::Ok().content_type("text/html").body(new_html)) -} diff --git a/crates/bwmapserver/src/ssr/allmaps.rs b/crates/bwmapserver/src/ssr/allmaps.rs deleted file mode 100644 index a95753d..0000000 --- a/crates/bwmapserver/src/ssr/allmaps.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/experiments/allmaps")] -async fn handler( - req: HttpRequest, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - #[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] - struct Map { - id: i64, - scenario_name: String, - last_modified: i64, - } - - let allow_nsfw = bwcommon::check_auth4(&req, (**pool).clone()) - .await? - .is_some(); - - let maps = pool.get().await?.query(" - select * from ( - select distinct map.id, map.denorm_scenario, uploaded_time, min(filetime.modified_time) as modified_time from map - left join filetime on filetime.map = map.id - where (nsfw = false or $1) and outdated = false and unfinished = false and broken = false and blackholed = false and denorm_scenario is not null - group by map.id, map.denorm_scenario, uploaded_time) sq - order by random()", &[&allow_nsfw]).await?.into_iter().map(|row| - { - anyhow::Ok(Map { - id: row.try_get(0)?, - scenario_name: row.try_get(1)?, - last_modified: row.try_get::<_, Option>("modified_time")?.unwrap_or(-1), - }) - }).collect::, _>>()?; - - let new_html = hb.render( - "allmaps", - &json!({ - "search_results": serde_json::to_string(&maps)?, - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?; - - Ok(HttpResponse::Ok().content_type("text/html").body(new_html)) -} diff --git a/crates/bwmapserver/src/ssr/change_password.rs b/crates/bwmapserver/src/ssr/change_password.rs deleted file mode 100644 index 2fcec2c..0000000 --- a/crates/bwmapserver/src/ssr/change_password.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/change-password")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "change-password", - &json!({ - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/change_username.rs b/crates/bwmapserver/src/ssr/change_username.rs deleted file mode 100644 index e637732..0000000 --- a/crates/bwmapserver/src/ssr/change_username.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/change-username")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "change-username", - &json!({ - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/index.rs b/crates/bwmapserver/src/ssr/index.rs deleted file mode 100644 index 4baa26c..0000000 --- a/crates/bwmapserver/src/ssr/index.rs +++ /dev/null @@ -1,229 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::HttpMessage; -use actix_web::Result; -use actix_web::{get, web, HttpRequest, HttpResponse, Responder}; -use bwcommon::MyError; -use serde::{Deserialize, Serialize}; -use tracing::instrument; - -#[get("/uiv1")] -#[instrument(skip_all, name = "/")] -async fn handler( - req: HttpRequest, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - #[derive(Debug, Serialize, Deserialize)] - struct MapRow { - map_id: String, - scenario_name: String, - uploaded_time: i64, - views: Option, - downloads: Option, - last_viewed: Option, - last_downloaded: Option, - } - - let last_uploaded_maps = async { - let con = pool.get().await?; - - let ret: Result, anyhow::Error> = con.query( - "select id, denorm_scenario, uploaded_time, views, downloads, last_viewed, last_downloaded - from map - where denorm_scenario is not null and uploaded_by != 10 and nsfw = false and outdated = false and unfinished = false and broken = false and blackholed = false - order by uploaded_time desc limit 5 - ", &[]).await?.into_iter().map(|x| Ok(MapRow { - map_id: bwcommon::get_web_id_from_db_id(x.try_get(0)?, crate::util::SEED_MAP_ID)?, - scenario_name: x.try_get::<_, String>(1)?, - uploaded_time: x.try_get(2)?, - views: x.try_get(3)?, - downloads: x.try_get(4)?, - last_viewed: x.try_get(5)?, - last_downloaded: x.try_get(6)?, - })).collect(); - - anyhow::Ok(ret?) - }; - - let most_viewed_maps = async { - let con = pool.get().await?; - - let ret: Result, anyhow::Error> = con.query( - "select id, denorm_scenario, uploaded_time, views, downloads, last_viewed, last_downloaded - from map - where denorm_scenario is not null and nsfw = false and outdated = false and unfinished = false and broken = false and blackholed = false - order by views desc limit 5", &[]).await?.into_iter().map(|x| Ok(MapRow { - map_id: bwcommon::get_web_id_from_db_id(x.try_get(0)?, crate::util::SEED_MAP_ID)?, - scenario_name: x.try_get::<_, String>(1)?, - uploaded_time: x.try_get(2)?, - views: x.try_get(3)?, - downloads: x.try_get(4)?, - last_viewed: x.try_get(5)?, - last_downloaded: x.try_get(6)?, - })).collect(); - - anyhow::Ok(ret?) - }; - - let most_downloaded_maps = async { - let con = pool.get().await?; - - let ret: Result, anyhow::Error> = con.query( - "select id, denorm_scenario, uploaded_time, views, downloads, last_viewed, last_downloaded - from map - where denorm_scenario is not null and nsfw = false and outdated = false and unfinished = false and broken = false and blackholed = false - order by downloads desc limit 5", &[]).await?.into_iter().map(|x| Ok(MapRow { - map_id: bwcommon::get_web_id_from_db_id(x.try_get(0)?, crate::util::SEED_MAP_ID)?, - scenario_name: x.try_get::<_, String>(1)?, - uploaded_time: x.try_get(2)?, - views: x.try_get(3)?, - downloads: x.try_get(4)?, - last_viewed: x.try_get(5)?, - last_downloaded: x.try_get(6)?, - })).collect(); - - anyhow::Ok(ret?) - }; - - let last_viewed_maps = async { - let con = pool.get().await?; - - let ret: Result, anyhow::Error> = con.query( - "select id, denorm_scenario, uploaded_time, views, downloads, last_viewed, last_downloaded - from map - where denorm_scenario is not null and last_viewed is not null and nsfw = false and outdated = false and unfinished = false and broken = false and blackholed = false - order by last_viewed desc limit 5", &[]).await?.into_iter().map(|x| Ok(MapRow { - map_id: bwcommon::get_web_id_from_db_id(x.try_get(0)?, crate::util::SEED_MAP_ID)?, - scenario_name: x.try_get::<_, String>(1)?, - uploaded_time: x.try_get(2)?, - views: x.try_get(3)?, - downloads: x.try_get(4)?, - last_viewed: x.try_get(5)?, - last_downloaded: x.try_get(6)?, - })).collect(); - - anyhow::Ok(ret?) - }; - - let last_downloaded_maps = async { - let con = pool.get().await?; - - let ret: Result, anyhow::Error> = con.query( - "select id, denorm_scenario, uploaded_time, views, downloads, last_viewed, last_downloaded - from map - where denorm_scenario is not null and last_downloaded is not null and nsfw = false and outdated = false and unfinished = false and broken = false and blackholed = false - order by last_downloaded desc limit 5", &[]).await?.into_iter().map(|x| Ok(MapRow { - map_id: bwcommon::get_web_id_from_db_id(x.try_get(0)?, crate::util::SEED_MAP_ID)?, - scenario_name: x.try_get::<_, String>(1)?, - uploaded_time: x.try_get(2)?, - views: x.try_get(3)?, - downloads: x.try_get(4)?, - last_viewed: x.try_get(5)?, - last_downloaded: x.try_get(6)?, - })).collect(); - - anyhow::Ok(ret?) - }; - - #[derive(Debug, Serialize, Deserialize)] - struct ReplayRow { - replay_id: i64, - scenario_name: String, - uploaded_time: i64, - } - - let last_uploaded_replays = async { - let con = pool.get().await?; - - let ret: Result, anyhow::Error> = con.query( - "select replay.id, map.denorm_scenario, replay.uploaded_time - from replay - join map on map.chkblob = replay.chkhash - where map.denorm_scenario is not null and nsfw = false and outdated = false and unfinished = false and broken = false and blackholed = false - order by uploaded_time desc limit 5", &[]).await?.into_iter().map(|x| Ok(ReplayRow { - replay_id: x.try_get(0)?, - scenario_name: crate::util::sanitize_sc_string(x.try_get::<_, String>(1)?.as_str()), - uploaded_time: x.try_get(2)?, - })).collect(); - - anyhow::Ok(ret?) - }; - let ( - last_uploaded_maps, - most_viewed_maps, - most_downloaded_maps, - last_viewed_maps, - last_downloaded_maps, - last_uploaded_replays, - ) = futures::try_join!( - last_uploaded_maps, - most_viewed_maps, - most_downloaded_maps, - last_viewed_maps, - last_downloaded_maps, - last_uploaded_replays, - )?; - - let langmap = if lang == bwcommon::LangData::Korean { - serde_json::json!({ - "h1": "scmscx.com에 오신 것을 환영합니다", - "h3": "우주에서 가장 큰 스타크래프트: 브루드 워 맵 데이터베이스", - "recently_viewed_maps": "최근에 본 지도", - "recently_downloaded_maps": "최근 다운로드한 지도", - "recently_uploaded_maps": "최근에 업로드한 지도", - "recently_uploaded_replays": "최근에 업로드된 리플레이", - "most_viewed_maps": "가장 많이 본 지도", - "most_downloaded_maps": "가장 많이 다운로드한 지도", - "navbar": get_navbar_langmap(lang) - }) - } else { - serde_json::json!({ - "h1": "Welcome to scmscx.com", - "h3": "The largest StarCraft: Brood War map database in the universe", - "recently_viewed_maps": "Recently Viewed Maps", - "recently_downloaded_maps": "Recently Downloaded Maps", - "recently_uploaded_maps": "Recently Uploaded Maps", - "recently_uploaded_replays": "Recently Uploaded Replays", - "most_viewed_maps": "Most Viewed Maps", - "most_downloaded_maps": "Most Downloaded Maps", - "navbar": get_navbar_langmap(lang) - }) - }; - - let new_html = hb.render( - "index", - &serde_json::json!({ - "last_viewed_maps": last_viewed_maps, - "most_viewed_maps": most_viewed_maps, - "last_downloaded_maps": last_downloaded_maps, - "most_downloaded_maps": most_downloaded_maps, - "last_uploaded_maps": last_uploaded_maps, - "last_uploaded_replays": last_uploaded_replays, - "langmap": langmap, - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?; - - Ok(HttpResponse::Ok() - .content_type("text/html") - .body(new_html) - .customize()) -} diff --git a/crates/bwmapserver/src/ssr/login.rs b/crates/bwmapserver/src/ssr/login.rs deleted file mode 100644 index b7f528d..0000000 --- a/crates/bwmapserver/src/ssr/login.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/login")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "login", - &json!({ - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/map.rs b/crates/bwmapserver/src/ssr/map.rs deleted file mode 100644 index 2e12386..0000000 --- a/crates/bwmapserver/src/ssr/map.rs +++ /dev/null @@ -1,1089 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpResponse, Responder}; -use actix_web::{HttpMessage, HttpRequest}; -use anyhow::Result; -use bwmap::ParsedChk; -use cached::proc_macro::cached; -use serde_json::json; -use std::sync::Arc; -use tracing::{info_span, instrument}; - -type KdTree = kiddo::KdTree; - -#[cached(key = "(i64)", convert = r#"{_len}"#, size = 1, result = true)] -async fn get_kd_tree( - _len: i64, - pool: bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, -) -> Result> { - let mut tree: KdTree = KdTree::new(); - - let con = pool.get().await?; - - let rows = con - .query( - "select map.id, ph16x16 from minimap join map on map.chkblob = minimap.chkhash", - &[], - ) - .await? - .into_iter() - .map(|x| { - let map_id = x.try_get::<_, i64>("id")?; - let ph32x32 = x.try_get::<_, Vec>("ph16x16")?; - - let mut ret = [0f32; 256]; - - for (index, x) in ph32x32.iter().enumerate() { - ret[index * 8] = (x & 0x01) as f32; - ret[index * 8 + 1] = ((x & 0x02) >> 1) as f32; - ret[index * 8 + 2] = ((x & 0x04) >> 2) as f32; - ret[index * 8 + 3] = ((x & 0x08) >> 3) as f32; - ret[index * 8 + 4] = ((x & 0x10) >> 4) as f32; - ret[index * 8 + 5] = ((x & 0x20) >> 5) as f32; - ret[index * 8 + 6] = ((x & 0x40) >> 6) as f32; - ret[index * 8 + 7] = ((x & 0x80) >> 7) as f32; - } - - anyhow::Ok((map_id, ret)) - }) - .collect::>>()?; - - for r in rows { - tree.add(&r.1, r.0)?; - } - - anyhow::Ok(Arc::new(tree)) -} - -// #[get("/api/similar_maps/{chkhash}")] -// #[instrument(skip_all)] -// pub async fn handler( -// path: web::Path<(String,)>, -// pool: web::Data< -// bb8_postgres::bb8::Pool< -// bb8_postgres::PostgresConnectionManager, -// >, -// >, -// ) -> Result { -// let (chkhash,) = path.into_inner(); - -// let con = pool.get().await?; -// let len: i64 = con -// .query_one("select count(*) from minimap", &[]) -// .instrument(tracing::info_span!("len").or_current()) -// .await? -// .try_get(0)?; -// let ph16x16: Vec = con -// .query_one( -// "select ph16x16 from minimap where chkhash = $1", -// &[&chkhash], -// ) -// .instrument(tracing::info_span!("ph16x16").or_current()) -// .await? -// .try_get(0)?; - -// let mut point = [0f32; 256]; -// for (index, x) in ph16x16.iter().enumerate() { -// point[index * 8 + 0] = ((x & 0x01) >> 0) as f32; -// point[index * 8 + 1] = ((x & 0x02) >> 1) as f32; -// point[index * 8 + 2] = ((x & 0x04) >> 2) as f32; -// point[index * 8 + 3] = ((x & 0x08) >> 3) as f32; -// point[index * 8 + 4] = ((x & 0x10) >> 4) as f32; -// point[index * 8 + 5] = ((x & 0x20) >> 5) as f32; -// point[index * 8 + 6] = ((x & 0x40) >> 6) as f32; -// point[index * 8 + 7] = ((x & 0x80) >> 7) as f32; -// } - -// let kd_tree = get_kd_tree(len, (**pool).clone()) -// .instrument(tracing::info_span!("get_kd_tree").or_current()) -// .await?; - -// //kd_tree.nearest(point, num, distance) - -// let info = ApiSpecificInfoForLogging { -// chk_hash: Some(chkhash.clone()), -// ..Default::default() -// }; - -// #[derive(Debug, Serialize, Deserialize)] -// struct SimilarChks { -// ph8x8: Vec, -// ph16x16: Vec, -// ph32x32: Vec, -// } - -// let nearest: Vec<_> = { -// let span = info_span!("nearest"); -// let _e = span.enter(); -// kd_tree -// .iter_nearest(&point, &|x, y| { -// x.into_iter() -// .zip(y) -// .fold(0.0, |a, (&b, &c)| a + (b - c).abs()) -// }) -// .map_err(|x| anyhow::Error::from(x))? -// .take_while(|x| x.0 <= 64.0) -// .take(20) -// .collect() -// }; - -// let mut arr = vec![]; - -// for (hamming_distance, map_id) in nearest { -// let pool = (**pool).clone(); - -// arr.push(async move { -// let con = pool.get().await?; - -// #[rustfmt::skip] -// let row = con.query_one(" -// select chkdenorm.scenario_name, chkdenorm.chkblob -// from map -// join chkdenorm on chkdenorm.chkblob = map.chkblob -// where map.id = $1", -// &[&map_id], -// ).await?; - -// anyhow::Ok(Chk { -// chkhash: row.try_get::<_, String>("chkblob")?, -// map_id: format!("{}", map_id), -// hamming_distance: hamming_distance as u64, -// scenario_name: row.try_get::<_, String>("scenario_name")?, -// }) -// }); -// } - -// #[derive(Debug, Serialize, Deserialize)] -// struct Chk { -// chkhash: String, -// map_id: String, -// hamming_distance: u64, -// scenario_name: String, -// } - -// let ret = futures::future::join_all(arr) -// .await -// .into_iter() -// .filter_map(|s| s.ok()) -// .collect(); - -// let similar_chks = SimilarChks { -// ph8x8: Vec::new(), -// ph16x16: Vec::new(), -// ph32x32: ret, -// }; - -// Ok(insert_extension(HttpResponse::Ok(), info) -// .content_type("application/json") -// .body(serde_json::to_string(&similar_chks).unwrap())) -// } - -#[instrument(skip_all, name = "/map")] -#[get("/uiv1/map/{map_id}")] -async fn handler( - req: HttpRequest, - path: web::Path<(String,)>, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - // convert IDs - let (map_id,) = path.into_inner(); - - let mut redirect = false; - - let map_id = if map_id.chars().all(|x| x.is_ascii_digit()) && map_id.len() < 8 { - redirect = true; - map_id.parse::()? - } else { - let map_id = bwcommon::get_db_id_from_web_id(&map_id, crate::util::SEED_MAP_ID); - if map_id.is_err() { - return Ok(HttpResponse::NotFound().finish().customize()); - } else { - map_id.unwrap() - } - }; - - tracing::info!("real map id: {}", map_id); - - { - let pool = pool.clone(); - let con = pool.get().await?; - let rows = con - .query_one("select count(*) from map where map.id = $1", &[&map_id]) - .await? - .try_get::<_, i64>(0)?; - - if rows == 0 { - return Ok(HttpResponse::NotFound().finish().customize()); - } else if redirect { - return Ok(HttpResponse::PermanentRedirect() - .finish() - .customize() - .insert_header(( - "Location", - format!( - "/map/{}", - bwcommon::get_web_id_from_db_id(map_id, crate::util::SEED_MAP_ID)? - ), - ))); - } - } - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct Filename { - filename: String, - } - - let current_user = bwcommon::check_auth4(&req, (**pool).clone()); - - let filenames = async { - let pool = pool.clone(); - let con = pool.get().await?; - anyhow::Ok( - con.query( - "select filename.filename - from mapfilename - join filename on mapfilename.filename = filename.id - where mapfilename.map = $1", - &[&map_id], - ) - .await? - .into_iter() - .map(|row| { - anyhow::Ok(Filename { - filename: row.try_get(0)?, - }) - }) - .collect::, _>>()?, - ) - }; - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct Filetime { - accessed_time: Option, - creation_time: Option, - modified_time: Option, - } - - let filetimes = async { - let pool = pool.clone(); - let con = pool.get().await?; - anyhow::Ok( - con.query( - "select accessed_time, creation_time, modified_time - from map - join filetime on filetime.map = map.id - where map.id = $1 - order by modified_time, creation_time, accessed_time", - &[&map_id], - ) - .await? - .into_iter() - .map(|row| { - anyhow::Ok(Filetime { - accessed_time: row.try_get(0)?, - creation_time: row.try_get(1)?, - modified_time: row.try_get(2)?, - }) - }) - .collect::, _>>()?, - ) - }; - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct Tag { - key: String, - value: String, - } - - let tags = async { - let pool = pool.clone(); - - let con = pool.get().await?; - anyhow::Ok( - con.query( - " - select key, value - from tagmap - join tag on tagmap.tag = tag.id - where tagmap.map = $1", - &[&map_id], - ) - .await? - .into_iter() - .map(|row| { - anyhow::Ok(Tag { - key: row.try_get(0)?, - value: row.try_get(1)?, - }) - }) - .collect::, _>>()?, - ) - }; - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct Flags { - nsfw: bool, - unfinished: bool, - broken: bool, - outdated: bool, - blackholed: bool, - } - let flags = async { - let pool = pool.clone(); - - let con = pool.get().await?; - let row = con - .query_one( - " - select nsfw, unfinished, broken, outdated, blackholed - from map - where map.id = $1", - &[&map_id], - ) - .await?; - - anyhow::Ok(Flags { - nsfw: row.try_get(0)?, - unfinished: row.try_get(1)?, - broken: row.try_get(2)?, - outdated: row.try_get(3)?, - blackholed: row.try_get(4)?, - }) - }; - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct ReplayInfo { - id: i64, - frames: i64, - time_saved: i64, - scenario_name: String, - creator: String, - } - - let replays = async { - let pool = pool.clone(); - let con = pool.get().await?; - anyhow::Ok(con.query(" - select replay.id, replay.denorm_frames, replay.denorm_time_saved, replay.denorm_scenario, replay.denorm_game_creator - from replay - join map on map.chkblob = replay.chkhash - where map.id = $1 - order by replay.denorm_frames", &[&map_id]).await?.into_iter().map(|r| { - anyhow::Ok(ReplayInfo { - id: r.try_get(0)?, - frames: r.try_get(1)?, - time_saved: r.try_get(2)?, - scenario_name: encoding_rs::UTF_8.decode(r.try_get::<_, Vec>(3)?.as_slice()).0.to_string(), - creator: encoding_rs::UTF_8.decode(r.try_get::<_, Vec>(4)?.as_slice()).0.to_string(), - }) - }).collect::, _>>()?) - }; - - let chkblob = async { - let pool = pool.clone(); - let con = pool.get().await?; - let row = con.query_one(" - select map.mapblob2, map.chkblob, mapblob_size, chkblob.length, chkblob.ver, chkblob.data, nsfw, uploaded_by, blackholed, views, downloads from map - join chkblob on chkblob.hash = map.chkblob - where map.id = $1", - &[&map_id]).await?; - - let mapblob_hash = row.try_get::<_, String>(0)?; - let chkhash = row.try_get::<_, String>(1)?; - let mapblob_size = row.try_get::<_, i64>(2)?; - let length = row.try_get::<_, i64>(3)? as usize; - let ver = row.try_get::<_, i64>(4)?; - let data = row.try_get::<_, Vec>(5)?; - let is_nsfw = row.try_get::<_, bool>(6)?; - let uploaded_by = row.try_get::<_, i64>(7)?; - let is_blackholed = row.try_get::<_, bool>(8)?; - let views = row.try_get::<_, i64>(9)?; - let downloads = row.try_get::<_, i64>(10)?; - - anyhow::ensure!(ver == 1); - - let chkblob = zstd::bulk::decompress(data.as_slice(), length)?; - - anyhow::Ok(( - mapblob_hash, - mapblob_size, - chkhash, - chkblob, - is_nsfw, - uploaded_by, - is_blackholed, - views, - downloads, - )) - }; - - let ph16x16 = async { - let pool = pool.clone(); - let con = pool.get().await?; - let row = con - .query_one( - " - select ph16x16 from map - join minimap on minimap.chkhash = map.chkblob - where map.id = $1", - &[&map_id], - ) - .await?; - - let mapblob_hash = row.try_get::<_, Vec>(0)?; - - let mut point = [0f32; 256]; - for (index, x) in mapblob_hash.into_iter().enumerate() { - point[index * 8] = (x & 0x01) as f32; - point[index * 8 + 1] = ((x & 0x02) >> 1) as f32; - point[index * 8 + 2] = ((x & 0x04) >> 2) as f32; - point[index * 8 + 3] = ((x & 0x08) >> 3) as f32; - point[index * 8 + 4] = ((x & 0x10) >> 4) as f32; - point[index * 8 + 5] = ((x & 0x20) >> 5) as f32; - point[index * 8 + 6] = ((x & 0x40) >> 6) as f32; - point[index * 8 + 7] = ((x & 0x80) >> 7) as f32; - } - - anyhow::Ok(point) - }; - - let denormalize_key = async { - let pool = pool.clone(); - let con = pool.get().await?; - let row = con - .query_one( - " - select count(*) from minimap", - &[], - ) - .await?; - - let denormalize_key = row.try_get::<_, i64>(0)?; - - anyhow::Ok(denormalize_key) - }; - - let ( - filenames, - tags, - chkblob, - replays, - flags, - current_user, - filetimes, - ph16x16, - denormalize_key, - ) = futures::try_join!( - filenames, - tags, - chkblob, - replays, - flags, - current_user, - filetimes, - ph16x16, - denormalize_key - )?; - - let ( - mapblob_hash, - mapblob_size, - chkhash, - chkblob, - is_nsfw, - uploaded_by, - is_blackholed, - views, - downloads, - ) = chkblob; - - if is_nsfw && current_user.is_none() { - return Ok(HttpResponse::Unauthorized().finish().customize()); - } - - if is_blackholed { - if let Some(user_id) = current_user { - if user_id != 4 { - return Ok(HttpResponse::Unauthorized().finish().customize()); - } - } else { - return Ok(HttpResponse::Unauthorized().finish().customize()); - } - } - - let parsed_chk = ParsedChk::from_bytes(chkblob.as_slice()); - - let kdtree = get_kd_tree(denormalize_key, (**pool).clone()).await?; - - let nearest: Vec<_> = { - let span = info_span!("nearest"); - let _e = span.enter(); - kdtree - .iter_nearest(&ph16x16, &|x, y| { - x.into_iter() - .zip(y) - .fold(0.0, |a, (&b, &c)| a + (b - c).abs()) - }) - .map_err(|x| anyhow::Error::from(x))? - .filter(|x| *x.1 != map_id) - .take_while(|x| x.0 <= 64.0) - .take(20) - .collect() - }; - - let mut similar_maps = vec![]; - - for (hamming_distance, map_id) in nearest { - let pool = (**pool).clone(); - - similar_maps.push(async move { - let con = pool.get().await?; - - #[rustfmt::skip] - let row = con.query_one(" - select - chkdenorm.scenario_name, - chkdenorm.chkblob, - chkdenorm.width, - chkdenorm.height, - chkdenorm.tileset, - map.nsfw, - map.outdated, - map.unfinished, - map.broken, - min(filetime.modified_time) as modified_time - from map - join chkdenorm on chkdenorm.chkblob = map.chkblob - left join filetime on filetime.map = map.id - where map.id = $1 - group by scenario_name, chkdenorm.chkblob, width, height, tileset, nsfw, outdated, unfinished, broken", - &[&map_id], - ).await?; - - let tileset = match row.try_get::<_, i64>("tileset")? % 8 { - 0 => "Badlands", - 1 => "Space Platform", - 2 => "Installation", - 3 => "Ashworld", - 4 => "Jungle", - 5 => "Desert", - 6 => "Arctic", - 7 => "Twilight", - _ => "Unknown tileset", - } - .to_owned(); - - if row.try_get::<_, bool>("nsfw")? || row.try_get::<_, bool>("outdated")? || row.try_get::<_, bool>("unfinished")? || row.try_get::<_, bool>("broken")? { - return Err(anyhow::anyhow!("nsfw")); - } - - anyhow::Ok(json!( { - "chkhash": row.try_get::<_, String>("chkblob")?, - "map_id": bwcommon::get_web_id_from_db_id(*map_id, crate::util::SEED_MAP_ID)?, - "hamming_distance": hamming_distance as u64, - "scenario_name": row.try_get::<_, String>("scenario_name")?, - "width": row.try_get::<_, i64>("width")?, - "height": row.try_get::<_, i64>("height")?, - "tileset": tileset, - "modified_time": row.try_get::<_, i64>("modified_time").unwrap_or(-1), - })) - }); - } - - let similar_maps: Vec<_> = futures::future::join_all(similar_maps) - .await - .into_iter() - .filter_map(|x| x.ok()) - // .map(|x| x.unwrap()) - .collect(); - - // hb.register_helper( - // "json", - // Box::new( - // |h: &handlebars::Helper, - // _r: &handlebars::Handlebars, - // _: &handlebars::Context, - // _rc: &mut handlebars::RenderContext, - // out: &mut dyn handlebars::Output| - // -> handlebars::HelperResult { - // let param = h - // .param(0) - // .ok_or(handlebars::RenderError::new("param not found"))?; - - // let obj = if let Some(obj) = param.value().as_object() { - // obj.clone().into_iter().filter(|x| x.0 != "type").collect() - // } else { - // serde_json::Map::::new() - // }; - - // out.write(&serde_json::to_string(&obj)?)?; - // Ok(()) - // }, - // ), - // ); - - // hb.register_helper( - // "debug", - // Box::new( - // |h: &handlebars::Helper, - // _r: &handlebars::Handlebars, - // _: &handlebars::Context, - // _rc: &mut handlebars::RenderContext, - // out: &mut dyn handlebars::Output| - // -> handlebars::HelperResult { - // let param = h - // .param(0) - // .ok_or(handlebars::RenderError::new("param not found"))?; - - // out.write(&format!("{:?}", param.value()))?; - // Ok(()) - // }, - // ), - // ); - - let (scenario_name, scenario_description) = if let Ok(x) = &parsed_chk.sprp { - let scenario_string = if *x.scenario_name_string_number == 0 { - "Untitled Scenario".to_owned() - } else { - if let Ok(s) = parsed_chk.get_string(*x.scenario_name_string_number as usize) { - s - } else { - "Failed to get scenario string".to_owned() - } - }; - - let scenario_description_string = if *x.description_string_number == 0 { - "".to_owned() - } else { - if let Ok(s) = parsed_chk.get_string(*x.description_string_number as usize) { - s - } else { - "Failed to get scenario description string string".to_owned() - } - }; - - (scenario_string, scenario_description_string) - } else { - ( - "Untitled Scenario".to_owned(), - ">>> No SPRP section in chk <<<<".to_owned(), - ) - }; - - let mut details = Vec::new(); - - if let Ok(x) = &parsed_chk.ver { - let version = match x.file_format_version { - 206 => "Remastered 1.21", - 205 => "Broodwar 1.04+", - 64 => "Starcraft Remastered 1.21 hybrid", - 63 => "Starcraft 1.04+ hybrid", - 59 => "Starcraft 1.00", - 61 | 75 | 201 | 203 => "BroodWar Internal", - 47 => "StarCraft Beta", - 0..=19 => "Warcraft II", - _ => "Unknown Version", - }; - details.push(json!([ - "Version", - format!("{} ({})", version, x.file_format_version) - ])); - } - - details.push(json!(["MPQ Hash", mapblob_hash])); - details.push(json!(["MPQ Size", mapblob_size])); - details.push(json!(["CHK Hash", chkhash])); - details.push(json!(["CHK Size", chkblob.len()])); - - let mut map_width = 0; - let mut map_height = 0; - - if let Ok(x) = &parsed_chk.dim { - map_width = *x.width; - map_height = *x.height; - details.push(json!([ - "Map Dimensions", - format!("{}x{}", x.width, x.height) - ])); - } - - let mut map_era = 0; - - if let Ok(x) = &parsed_chk.era { - map_era = x.tileset % 8; - details.push(json!([ - "Map Tileset", - format!( - "{} ({} mod 8 = {})", - match x.tileset % 8 { - 0 => "Badlands", - 1 => "Space Platform", - 2 => "Installation", - 3 => "Ashworld", - 4 => "Jungle", - 5 => "Desert", - 6 => "Arctic", - 7 => "Twilight", - _ => "Unknown tileset", - }, - x.tileset, - x.tileset % 8 - ) - ])); - } - - if let Ok(x) = &parsed_chk.ownr { - // u8[12]: One byte for each player, specifies the owner of the player: - // 00 - Inactive - // 01 - Computer (game) - // 02 - Occupied by Human Player - // 03 - Rescue Passive - // 04 - Unused - // 05 - Computer - // 06 - Human (Open Slot) - // 07 - Neutral - // 08 - Closed slot - details.push(json!([ - "Human Players", - x.player_owner.iter().filter(|&&x| x == 6).count() - ])); - - details.push(json!([ - "Computer Players", - x.player_owner.iter().filter(|&&x| x == 5).count() - ])); - - details.push(json!([ - "OWNR Players", - x.player_owner - .iter() - .fold("".to_owned(), |a, &b| format!("{}{}", a, b)) - ])); - } - - if let Ok(x) = &parsed_chk.dd2 { - details.push(json!(["Doodads", x.doodads.len()])); - } - - if let Ok(x) = &parsed_chk.thg2 { - details.push(json!(["Sprites", x.sprites.len()])); - } - - if let Ok(x) = &parsed_chk.trig { - details.push(json!(["Triggers", x.triggers.len()])); - } - - if let Ok(x) = &parsed_chk.mbrf { - details.push(json!(["Briefing Triggers", x.triggers.len()])); - } - - if let Ok(x) = &parsed_chk.mrgn { - details.push(json!([ - "Locations", - x.locations - .iter() - .filter(|&&x| !(x.left == x.right || x.top == x.bottom)) - .count() - ])); - } - - if let Ok(x) = &parsed_chk.unit { - details.push(json!(["Units", x.units.len()])); - } - - let mut mtxm = Vec::new(); - - if let Ok(x) = &parsed_chk.mtxm { - mtxm = x.data.clone(); - let hash_set: std::collections::HashSet = x.data.iter().cloned().collect(); - details.push(json!(["Unique Terrain Tiles", hash_set.len()])); - } - - let parsed_triggers = bwmap::parse_triggers(&parsed_chk); - - let mission_briefings = bwmap::parse_mission_briefing(&parsed_chk); - - { - let mut set = std::collections::HashSet::new(); - - for trigger in &parsed_triggers { - for action in &trigger.actions { - match action { - bwmap::Action::PlayWav { wave, wave_time: _ } => { - set.insert(wave); - } - bwmap::Action::Transmission { - text: _, - unit_type: _, - location: _, - time: _, - modifier: _, - wave, - wave_time: _, - } => { - set.insert(wave); - } - _ => {} - } - } - } - - for trigger in &mission_briefings { - for action in &trigger.actions { - match action { - bwmap::MissionBriefingAction::PlayWav { wave, wave_time: _ } => { - set.insert(wave); - } - bwmap::MissionBriefingAction::DisplayTransmission { - text: _, - slot: _, - time: _, - modifier: _, - wave, - wave_time: _, - } => { - set.insert(wave); - } - _ => {} - } - } - } - - details.push(json!(["Wav Files", set.len()])); - } - - let units = if let Ok(x) = &parsed_chk.unix { - let mut v = Vec::new(); - - for unit_id in 0..x.config.len() { - if x.config[unit_id] == 0 && x.string_number[unit_id] != 0 { - v.push(json!({ - "unit_id": unit_id, - "name": parsed_chk.get_string(x.string_number[unit_id] as usize) - .unwrap_or("couldn't decode string".to_owned()), - })); - } - } - - v - } else if let Ok(x) = &parsed_chk.unis { - let mut v = Vec::new(); - - for unit_id in 0..x.config.len() { - if x.config[unit_id] == 0 && x.string_number[unit_id] != 0 { - v.push(json!({ - "unit_id": unit_id, - "name": parsed_chk.get_string(x.string_number[unit_id] as usize) - .unwrap_or("couldn't decode string".to_owned()), - })); - } - } - - v - } else { - Vec::new() - }; - - let forces = if let Ok(x) = &parsed_chk.forc { - // pub player_forces: &'a [u8], - // pub force_name: &'a [u16], - // pub force_properties: &'a [u8], - - let mut v = Vec::new(); - - let mut force_number = 1; - - for force in x.force_name { - if force == 0 { - v.push(json!({ "force": format!("Force {force_number}") })) - } else { - v.push(json!({ - "force": parsed_chk.get_string(force as usize).unwrap_or("couldn't get force name".to_owned()) - })) - } - force_number += 1; - } - - v - } else { - Vec::new() - }; - - { - #[allow(non_snake_case)] - let mut get_deaths_EUDs: i32 = 0; - #[allow(non_snake_case)] - let mut get_deaths_EPDs: i32 = 0; - #[allow(non_snake_case)] - let mut set_deaths_EUDs: i32 = 0; - #[allow(non_snake_case)] - let mut set_deaths_EPDs: i32 = 0; - #[allow(non_snake_case)] - let mut EUPs: i32 = 0; - - if let Ok(x) = parsed_chk.unit { - for unit in &x.units { - if unit.unit_id > 227 || unit.owner > 27 { - EUPs += 1; - } - } - } - - for trigger in &parsed_triggers { - for condition in &trigger.conditions { - match condition { - bwmap::Condition::Deaths { - player, - comparison: _, - unit_type, - number: _, - eud_offset: _, - } => { - match player { - bwmap::Group::Unknown(_) => { - get_deaths_EPDs += 1; - } - _ => {} - } - match unit_type { - bwmap::UnitType::Unknown(_) => { - get_deaths_EUDs += 1; - } - _ => {} - } - } - _ => {} - } - } - for action in &trigger.actions { - match action { - bwmap::Action::SetDeaths { - player, - unit_type, - number: _, - modifier: _, - eud_offset: _, - } => { - match player { - bwmap::Group::Unknown(_) => { - set_deaths_EPDs += 1; - } - _ => {} - } - match unit_type { - bwmap::UnitType::Unknown(_) => { - set_deaths_EUDs += 1; - } - _ => {} - } - } - _ => {} - } - } - } - - details.push(json!(["GetDeaths EUDs", get_deaths_EUDs])); - details.push(json!(["GetDeaths EPDs", get_deaths_EPDs])); - details.push(json!(["SetDeaths EUDs", set_deaths_EUDs])); - details.push(json!(["SetDeaths EPDs", set_deaths_EPDs])); - details.push(json!(["EUPs", EUPs])); - - details.push(json!(["Views", views])); - details.push(json!(["Downloads", downloads])); - } - - let details: Vec<_> = details - .into_iter() - .map(|x| json!({"key": x[0], "value": x[1]})) - .collect(); - - // let minimap_scaling_factor = f32::min(512.0 / map_width as f32, 512.0 / map_height as f32); - - // let minimap_width = map_width as f32 * minimap_scaling_factor; - // let minimap_height = map_width as f32 * minimap_scaling_factor; - - let new_html = info_span!("rendering").in_scope(|| { - anyhow::Ok(hb.render( - "map", - &json!({ - "minimap_id": chkhash.clone(), - "minimap_width": map_width, - "minimap_height": map_height, - "sanitized_scenario_name": crate::util::sanitize_sc_string(scenario_name.as_str()), - "sanitized_scenario_description": crate::util::sanitize_sc_string_preserve_newlines(scenario_description.as_str()), - "scenario_name": scenario_name, - "scenario_description": scenario_description, - "filenames": serde_json::to_string(&filenames)?, - "filenames2": filenames, - "tags": serde_json::to_string(&tags)?, - "replays2": replays, - "flags": serde_json::to_string(&flags)?, - "filetimes": serde_json::to_string(&filetimes)?, - "filetimes2": filetimes, - "show_flags": current_user.is_some() && (current_user.unwrap() == uploaded_by || current_user.unwrap() == 4), - "details": details, - "mapblob_hash": mapblob_hash, - "mapblob_size": mapblob_size, - "chkblob_hash": chkhash.clone(), - "units": serde_json::to_string(&units)?, - "units2": units, - "forces": serde_json::to_string(&forces)?, - "forces2": forces, - "mtxm": mtxm, - "map_width": serde_json::to_string(&map_width)?, - "map_height": serde_json::to_string(&map_height)?, - "map_era": serde_json::to_string(&map_era)?, - "download_size": format!("{} KB", mapblob_size / 1024), - "download_filename": filenames[0].filename, - "similar_maps": similar_maps, - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?) - })?; - - { - let time_since_epoch = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH)? - .as_secs() as i64; - - let pool = pool.clone(); - let con = pool.get().await?; - let rows = con - .execute( - "update map set views = views + 1, last_viewed = $1 where map.id = $2", - &[&time_since_epoch, &map_id], - ) - .await?; - - (|| { - anyhow::ensure!(rows == 1); - anyhow::Ok(()) - })()?; - } - - Ok(HttpResponse::Ok() - .content_type("text/html") - .body(new_html) - .customize()) -} diff --git a/crates/bwmapserver/src/ssr/mod.rs b/crates/bwmapserver/src/ssr/mod.rs deleted file mode 100644 index 3e5f8ed..0000000 --- a/crates/bwmapserver/src/ssr/mod.rs +++ /dev/null @@ -1,48 +0,0 @@ -pub mod about; -pub mod allmaps; -pub mod change_password; -pub mod change_username; -pub mod index; -pub mod login; -pub mod map; -pub mod recent; -pub mod register; -pub mod replay; -pub mod search; -// pub mod search2; -pub mod upload_map; -pub mod upload_replay; -pub mod user; -pub mod viewer; - -fn get_navbar_langmap(lang: bwcommon::LangData) -> serde_json::Value { - if lang == bwcommon::LangData::Korean { - serde_json::json!({ - "home": "홈으로", - "search": "검색", - "recent": "최신순", - "about": "정보", - "upload_map": "지도 업로드", - "upload_replay": "다시보기 업로드", - "login": "로그인", - "register": "가입하기", - "change_username": "사용자 이름 변경", - "change_password": "비밀번호 변경", - "logout": "로그 아웃", - }) - } else { - serde_json::json!({ - "home": "Home", - "search": "Search", - "recent": "Recent", - "about": "About", - "upload_map": "Upload Map", - "upload_replay": "Upload Replay", - "login": "Log in", - "register": "Register", - "change_username": "Change Username", - "change_password": "Change Password", - "logout": "Log out", - }) - } -} diff --git a/crates/bwmapserver/src/ssr/recent.rs b/crates/bwmapserver/src/ssr/recent.rs deleted file mode 100644 index 8390e58..0000000 --- a/crates/bwmapserver/src/ssr/recent.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/recent")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "recent", - &json!({ - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/register.rs b/crates/bwmapserver/src/ssr/register.rs deleted file mode 100644 index 82e2397..0000000 --- a/crates/bwmapserver/src/ssr/register.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/register")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "register", - &json!({ - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/replay.rs b/crates/bwmapserver/src/ssr/replay.rs deleted file mode 100644 index da2890a..0000000 --- a/crates/bwmapserver/src/ssr/replay.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde::{Deserialize, Serialize}; -use serde_json::json; - -#[get("/uiv1/replay/{replay_id}")] -async fn handler( - req: HttpRequest, - path: web::Path<(i64,)>, - hb: web::Data>, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - let (replay_id,) = path.into_inner(); - - let (uploaded_by, uploaded_time, replay_blob, denorm_scenario, chkhash, map_id) = { - let r = pool.get().await?.query_one(" - select account.username, replay.uploaded_time, replayblob.data, map.denorm_scenario, replay.chkhash, map.id - from replay - join replayblob on replayblob.hash = replay.hash - join account on account.id = uploaded_by - full outer join map on map.chkblob = replay.chkhash - where replay.id = $1", &[&replay_id]).await?; - - ( - r.try_get::<_, String>(0)?, - r.try_get::<_, i64>(1)?, - r.try_get::<_, Vec>(2)?, - r.try_get::<_, String>(3)?, - r.try_get::<_, String>(4)?, - r.try_get::<_, i64>(5)?, - ) - }; - - #[derive(Debug, Serialize, Deserialize)] - struct ReplayInfo { - uploaded_by: String, - uploaded_time: i64, - replay_header: bwreplay::ReplayHeader, - denorm_scenario: String, - chkhash: String, - map_id: i64, - } - - let ret = ReplayInfo { - uploaded_by, - uploaded_time, - replay_header: bwreplay::parse_replay_blob(replay_blob.as_slice())?.header, - denorm_scenario, - chkhash, - map_id, - }; - - // ret.replay_header.slots_players[0]. - - let players: Vec<_> = ret - .replay_header - .slots_players - .iter() - .map(|x| { - json!({ - "player_id": x.player_id, - "player_name": std::str::from_utf8(x.player_name.as_slice()).unwrap_or("Couldn't decode utf8 player name"), - "player_race": x.player_race, - "player_team": x.player_team, - "player_type": x.player_type, - "slot_id": x.slot_id, - }) - }) - .collect(); - - // ret.replay_header - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "replay", - &json!({ - "scenario_name": ret.denorm_scenario, - "chkhash": ret.chkhash, - "map_id": ret.map_id, - "uploaded_time": ret.uploaded_time, - "uploaded_by": ret.uploaded_by, - "frames": ret.replay_header.frames, - "time_save": ret.replay_header.time_save, - "height": ret.replay_header.height, - "width": ret.replay_header.width, - "game_name": std::str::from_utf8(ret.replay_header.name_game.as_slice()).unwrap_or("Couldn't utf8 decode game name"), - "gamespeed": ret.replay_header.speed, - "game_type": ret.replay_header.game_type, - "game_subtype": ret.replay_header.game_subtype, - "engine": ret.replay_header.engine, - "available_slots": ret.replay_header.count_available_slots, - "lobby_creator": std::str::from_utf8(ret.replay_header.name_creator.as_slice()).unwrap_or("Couldn't utf8 decode creator name"), - "players": players, - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/search.rs b/crates/bwmapserver/src/ssr/search.rs deleted file mode 100644 index b439920..0000000 --- a/crates/bwmapserver/src/ssr/search.rs +++ /dev/null @@ -1,214 +0,0 @@ -use crate::middleware::UserSession; -use crate::search2::{search, SearchParams}; -use crate::ssr::get_navbar_langmap; -use actix_web::HttpMessage; -use actix_web::{get, web, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use tracing::instrument; - -// trait MyTrait: r2d2_postgres::postgres::types::ToSql + Sync + std::fmt::Display { -// fn to_sub(&self) -> &(dyn r2d2_postgres::postgres::types::ToSql + Sync); -// } - -// impl MyTrait for T { -// fn to_sub(&self) -> &(dyn r2d2_postgres::postgres::types::ToSql + Sync) { -// self -// } -// } - -// trait SuperCached { -// fn cached_query( -// &mut self, -// query: &str, -// params: &[&dyn MyTrait], -// cache: &std::sync::Mutex>>, -// map: F, -// ) -> Result, E> -// where -// F: FnMut(r2d2_postgres::postgres::row::Row) -> Result, -// T: Clone, -// E: From; -// } - -// impl SuperCached -// for r2d2_postgres::r2d2::PooledConnection< -// r2d2_postgres::PostgresConnectionManager, -// > -// { -// fn cached_query( -// &mut self, -// query: &str, -// params: &[&dyn MyTrait], -// cache: &std::sync::Mutex>>, -// map: F, -// ) -> Result, E> -// where -// F: FnMut(r2d2_postgres::postgres::row::Row) -> Result, -// T: Clone, -// E: From, -// { -// let mut key = format!("{}", query); - -// for v in params { -// key = format!("{}+{}", key, v); -// } - -// { -// let mut guard = cache.lock().unwrap(); -// if let Some(cached_value) = guard.get(&key.to_string()) { -// return Ok(cached_value.clone()); -// } -// } - -// let mut v: Vec<&(dyn r2d2_postgres::postgres::types::ToSql + Sync)> = Vec::new(); -// for k in params { -// v.push(k.to_sub()); -// } -// let vec = self -// .query(query, v.as_slice())? -// .into_iter() -// .map(map) -// .collect::, E>>()?; - -// if let Ok(mut guard) = cache.lock() { -// guard.put(key, vec.clone()); -// } - -// Ok(vec) -// } -// } - -#[instrument(skip_all, name = "/search")] -async fn handler2( - req: HttpRequest, - query: String, - query_params: web::Query, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - // lazy_static::lazy_static! { - // static ref CACHE: std::sync::Mutex>> = { - // std::sync::Mutex::new(lru::LruCache::>::new(100)) - // }; - // } - - // let needs_drop = { - // let mut guard = cache_droper.lock().unwrap(); - // guard.insert(format!("{}{}", file!(), line!())) - // }; - - // if needs_drop { - // CACHE.lock().unwrap().clear(); - // } - - // cache_droper: actix_web::web::Data>>, - - let allow_nsfw = bwcommon::check_auth4(&req, (**pool).clone()) - .await? - .is_some(); - - let maps = search(query.as_str(), allow_nsfw, &query_params, pool.clone()).await?; - - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - let page_title = if query.is_empty() { - "Search StarCraft: Brood War Maps".to_owned() - } else { - format!("{} maps found for: {}", maps.len(), query) - }; - - let langmap = if lang == bwcommon::LangData::Korean { - serde_json::json!({ - "h1": "데이터베이스의 100,000개 지도에서 지도 이름, 부대 이름, 설명 및 부대 이름 검색", - "h4_try_popular_searches": "인기 검색어 시도", - "h4_did_you_make_maps": "지도를 만드셨나요? 사용한 이름을 검색해 보세요", - "random_button": "무작위의", - "search_button": "검색", - "unit_names": "단위 이름", - "force_names": "포스 이름", - "file_names": "파일 이름", - "scenario_names": "시나리오 이름", - "scenario_descriptions": "시나리오 설명", - "results": "결과", - "scenario": "대본", - "last_modified_time": "마지막 수정 시간", - "uploaded_time": "업로드 시간", - "navbar": get_navbar_langmap(lang), - }) - } else { - serde_json::json!({ - "h1": "Search map names, unit names, descriptions, and force names across over 100,000 maps in the database", - "h4_try_popular_searches": "Try Popular searches", - "h4_did_you_make_maps": "Did you make maps? Try searching the name you used", - "random_button": "Random", - "search_button": "Search", - "unit_names": "Unit Names", - "force_names": "Force Names", - "file_names": "File Names", - "scenario_names": "Scenario Names", - "scenario_descriptions": "Scenario Descriptions", - "results": "Results", - "scenario": "Scenario", - "last_modified_time": "Last Modified Time", - "uploaded_time": "Uploaded Time", - "navbar": get_navbar_langmap(lang), - }) - }; - - let new_html = hb.render( - "search", - &serde_json::json!({ - "page_title": page_title, - "search_results": serde_json::to_string(&maps)?, - "langmap": langmap, - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?; - - Ok(HttpResponse::Ok().content_type("text/html").body(new_html)) -} - -#[get("/uiv1/search/{query}")] -async fn handler( - req: HttpRequest, - path: web::Path<(String,)>, - query_params: web::Query, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - let (query,) = path.into_inner(); - handler2(req, query, query_params, pool, hb).await -} - -#[get("/uiv1/search")] -async fn handler_empty_query( - req: HttpRequest, - query_params: web::Query, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - handler2(req, "".to_string(), query_params, pool, hb).await -} diff --git a/crates/bwmapserver/src/ssr/search2.rs b/crates/bwmapserver/src/ssr/search2.rs deleted file mode 100644 index 4b7fe58..0000000 --- a/crates/bwmapserver/src/ssr/search2.rs +++ /dev/null @@ -1,432 +0,0 @@ -use actix_web::HttpMessage; -use actix_web::{get, web, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde::{Deserialize, Serialize}; -use tracing::instrument; -use std::sync::{Mutex}; -use crate::search::SearchDatabase; -use std::collections::HashSet; - -// trait MyTrait: r2d2_postgres::postgres::types::ToSql + Sync + std::fmt::Display { -// fn to_sub(&self) -> &(dyn r2d2_postgres::postgres::types::ToSql + Sync); -// } - -// impl MyTrait for T { -// fn to_sub(&self) -> &(dyn r2d2_postgres::postgres::types::ToSql + Sync) { -// self -// } -// } - -// trait SuperCached { -// fn cached_query( -// &mut self, -// query: &str, -// params: &[&dyn MyTrait], -// cache: &std::sync::Mutex>>, -// map: F, -// ) -> Result, E> -// where -// F: FnMut(r2d2_postgres::postgres::row::Row) -> Result, -// T: Clone, -// E: From; -// } - -// impl SuperCached -// for r2d2_postgres::r2d2::PooledConnection< -// r2d2_postgres::PostgresConnectionManager, -// > -// { -// fn cached_query( -// &mut self, -// query: &str, -// params: &[&dyn MyTrait], -// cache: &std::sync::Mutex>>, -// map: F, -// ) -> Result, E> -// where -// F: FnMut(r2d2_postgres::postgres::row::Row) -> Result, -// T: Clone, -// E: From, -// { -// let mut key = format!("{}", query); - -// for v in params { -// key = format!("{}+{}", key, v); -// } - -// { -// let mut guard = cache.lock().unwrap(); -// if let Some(cached_value) = guard.get(&key.to_string()) { -// return Ok(cached_value.clone()); -// } -// } - -// let mut v: Vec<&(dyn r2d2_postgres::postgres::types::ToSql + Sync)> = Vec::new(); -// for k in params { -// v.push(k.to_sub()); -// } -// let vec = self -// .query(query, v.as_slice())? -// .into_iter() -// .map(map) -// .collect::, E>>()?; - -// if let Ok(mut guard) = cache.lock() { -// guard.put(key, vec.clone()); -// } - -// Ok(vec) -// } -// } - -fn defaulttrue() -> bool { - true -} - -fn default0() -> i64 { - 0 -} - -fn default256() -> i64 { - 256 -} - -fn default12() -> i64 { - 256 -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -struct SearchQuery { - #[serde(default = "defaulttrue")] - pub(crate) unit_names: bool, - #[serde(default = "defaulttrue")] - pub(crate) force_names: bool, - #[serde(default = "defaulttrue")] - pub(crate) file_names: bool, - #[serde(default = "defaulttrue")] - pub(crate) scenario_names: bool, - #[serde(default = "defaulttrue")] - pub(crate) scenario_descriptions: bool, - #[serde(default = "defaulttrue")] - pub(crate) provided_by: bool, - - #[serde(default = "default0")] - pub(crate) minimum_map_width: i64, - #[serde(default = "default256")] - pub(crate) maximum_map_width: i64, - #[serde(default = "default0")] - pub(crate) minimum_map_height: i64, - #[serde(default = "default256")] - pub(crate) maximum_map_height: i64, - - #[serde(default = "defaulttrue")] - pub(crate) tileset_badlands: bool, - #[serde(default = "defaulttrue")] - pub(crate) tileset_space_platform: bool, - #[serde(default = "defaulttrue")] - pub(crate) tileset_installation: bool, - #[serde(default = "defaulttrue")] - pub(crate) tileset_ashworld: bool, - #[serde(default = "defaulttrue")] - pub(crate) tileset_jungle: bool, - #[serde(default = "defaulttrue")] - pub(crate) tileset_desert: bool, - #[serde(default = "defaulttrue")] - pub(crate) tileset_ice: bool, - #[serde(default = "defaulttrue")] - pub(crate) tileset_twilight: bool, - - #[serde(default = "default0")] - pub(crate) minimum_human_players: i64, - #[serde(default = "default12")] - pub(crate) maximum_human_players: i64, - #[serde(default = "default0")] - pub(crate) minimum_computer_players: i64, - #[serde(default = "default12")] - pub(crate) maximum_computer_players: i64, -} - -#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] -struct Map { - id: String, - scenario_name: String, - last_modified: i64, - uploaded_time: i64, -} - -#[instrument(skip_all, name = "/search2")] -async fn handler2( - req: HttpRequest, - query: String, - query_params: web::Query, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, - searchdb: web::Data>, -) -> Result { - // lazy_static::lazy_static! { - // static ref CACHE: std::sync::Mutex>> = { - // std::sync::Mutex::new(lru::LruCache::>::new(100)) - // }; - // } - - // let needs_drop = { - // let mut guard = cache_droper.lock().unwrap(); - // guard.insert(format!("{}{}", file!(), line!())) - // }; - - // if needs_drop { - // CACHE.lock().unwrap().clear(); - // } - - // cache_droper: actix_web::web::Data>>, - - let mut allowed_tilesets: Vec = Vec::new(); - - if query_params.tileset_badlands { - allowed_tilesets.push(0); - } - if query_params.tileset_space_platform { - allowed_tilesets.push(1); - } - if query_params.tileset_installation { - allowed_tilesets.push(2); - } - if query_params.tileset_ashworld { - allowed_tilesets.push(3); - } - if query_params.tileset_jungle { - allowed_tilesets.push(4); - } - if query_params.tileset_desert { - allowed_tilesets.push(5); - } - if query_params.tileset_ice { - allowed_tilesets.push(6); - } - if query_params.tileset_twilight { - allowed_tilesets.push(7); - } - - let allow_nsfw = bwcommon::check_auth4(&req, (**pool).clone()) - .await? - .is_some(); - - let maps = if query.is_empty() { - let con = pool.get().await?; - - con.query(" - select distinct map.id, chkdenorm.scenario_name, uploaded_time, min(filetime.modified_time) as modified_time, uploaded_time, chkdenorm.width, chkdenorm.height, chkdenorm.tileset, chkdenorm.human_players, chkdenorm.computer_players from map - left join filetime on filetime.map = map.id - join chkdenorm on chkdenorm.chkblob = map.chkblob - where - nsfw = false and - outdated = false and - unfinished = false and - broken = false and - blackholed = false and - chkdenorm.scenario_name is not null and - chkdenorm.width >= $1 and chkdenorm.width <= $2 and - chkdenorm.height >= $3 and chkdenorm.height <= $4 and - chkdenorm.tileset = any($5) and - chkdenorm.human_players >= $6 and chkdenorm.human_players <= $7 and - chkdenorm.computer_players >= $8 and chkdenorm.computer_players <= $9 - group by map.id, chkdenorm.scenario_name, uploaded_time, chkdenorm.width, chkdenorm.height, chkdenorm.tileset, chkdenorm.human_players, chkdenorm.computer_players - order by uploaded_time desc - limit 5000 - ", &[ - &query_params.minimum_map_width, - &query_params.maximum_map_width, - &query_params.minimum_map_height, - &query_params.maximum_map_height, - &allowed_tilesets, - &query_params.minimum_human_players, - &query_params.maximum_human_players, - &query_params.minimum_computer_players, - &query_params.maximum_computer_players, - ]).await?.into_iter().map(|row| - { - anyhow::Ok(Map { - id: bwcommon::get_web_id_from_db_id(row.try_get(0)?, crate::util::SEED_MAP_ID)?, - scenario_name: row.try_get(1)?, - last_modified: row.try_get::<_, Option>("modified_time")?.unwrap_or(-1), - uploaded_time: row.try_get::<_, i64>("uploaded_time")?, - }) - }).collect::, _>>()? - } else { - - let mut map_ids: HashSet = HashSet::new(); - - if query_params.scenario_names { - let lock = searchdb.lock().unwrap(); - map_ids.extend(lock.scenario_name.get(query.as_str()).iter()); - } - - if query_params.scenario_descriptions { - let lock = searchdb.lock().unwrap(); - map_ids.extend(lock.scenario_description.get(query.as_str()).iter()); - } - - if query_params.unit_names { - let lock = searchdb.lock().unwrap(); - map_ids.extend(lock.unit_names.get(query.as_str()).iter()); - } - - if query_params.force_names { - let lock = searchdb.lock().unwrap(); - map_ids.extend(lock.force_names.get(query.as_str()).iter()); - } - - if query_params.file_names { - let lock = searchdb.lock().unwrap(); - map_ids.extend(lock.file_names.get(query.as_str()).iter()); - } - - let map_ids: Vec<_> = map_ids.into_iter().collect(); - - let con = pool.get().await?; - - let qs = - " - select mapid as id, scenario_name, modified_time, uploaded_time from ( - select map.id as mapid, chkdenorm.scenario_name, min(filetime.modified_time) as modified_time, map.uploaded_time from ( - select unnest($1::int8[]) as mapid - ) as sq - join map on map.id = sq.mapid - left join filetime on filetime.map = map.id - join chkdenorm on chkdenorm.chkblob = map.chkblob - where ($2 or map.nsfw = false) and outdated = false and unfinished = false and broken = false and - chkdenorm.scenario_name is not null and - chkdenorm.width >= $3 and chkdenorm.width <= $4 and - chkdenorm.height >= $5 and chkdenorm.height <= $6 and - chkdenorm.tileset = any($7) and - chkdenorm.human_players >= $8 and chkdenorm.human_players <= $9 and - chkdenorm.computer_players >= $10 and chkdenorm.computer_players <= $11 - group by map.id, chkdenorm.scenario_name - -- order by dist2 desc - ) as sq3"; - - con.query( - qs, - &[ - &map_ids, - &allow_nsfw, - &query_params.minimum_map_width, - &query_params.maximum_map_width, - &query_params.minimum_map_height, - &query_params.maximum_map_height, - &allowed_tilesets, - &query_params.minimum_human_players, - &query_params.maximum_human_players, - &query_params.minimum_computer_players, - &query_params.maximum_computer_players, - ], - ) - .await? - .into_iter() - .map(|row| { - anyhow::Ok(Map { - id: bwcommon::get_web_id_from_db_id(row.try_get(0)?, crate::util::SEED_MAP_ID)?, - scenario_name: row.try_get(1)?, - last_modified: row.try_get::<_, Option>(2)?.unwrap_or(-1), - uploaded_time: row.try_get::<_, i64>(3)?, - }) - }) - .collect::, _>>()? - }; - - let lang = req - .extensions() - .get::() - .clone() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let page_title = if query.is_empty() { - "Search StarCraft: Brood War Maps".to_owned() - } else { - format!("{} maps found for: {}", maps.len(), query) - }; - - let langmap = if lang == bwcommon::LangData::Korean { - serde_json::json!({ - "h1": "데이터베이스의 100,000개 지도에서 지도 이름, 부대 이름, 설명 및 부대 이름 검색", - "h4_try_popular_searches": "인기 검색어 시도", - "h4_did_you_make_maps": "지도를 만드셨나요? 사용한 이름을 검색해 보세요", - "random_button": "무작위의", - "search_button": "검색", - "unit_names": "단위 이름", - "force_names": "포스 이름", - "file_names": "파일 이름", - "scenario_names": "시나리오 이름", - "scenario_descriptions": "시나리오 설명", - "results": "결과", - "scenario": "대본", - "last_modified_time": "마지막 수정 시간", - "uploaded_time": "업로드 시간", - }) - } else { - serde_json::json!({ - "h1": "Search map names, unit names, descriptions, and force names across over 100,000 maps in the database", - "h4_try_popular_searches": "Try Popular searches", - "h4_did_you_make_maps": "Did you make maps? Try searching the name you used", - "random_button": "Random", - "search_button": "Search", - "unit_names": "Unit Names", - "force_names": "Force Names", - "file_names": "File Names", - "scenario_names": "Scenario Names", - "scenario_descriptions": "Scenario Descriptions", - "results": "Results", - "scenario": "Scenario", - "last_modified_time": "Last Modified Time", - "uploaded_time": "Uploaded Time", - }) - }; - - let new_html = hb.render( - "search", - &serde_json::json!({ - "page_title": page_title, - "search_results": serde_json::to_string(&maps)?, - "langmap": langmap, - }), - )?; - - Ok(HttpResponse::Ok().content_type("text/html").body(new_html)) -} - -#[get("/uiv1/search2/{query}")] -async fn handler( - req: HttpRequest, - path: web::Path<(String,)>, - query_params: web::Query, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, - searchdb: web::Data>, -) -> Result { - let (query,) = path.into_inner(); - handler2(req, query, query_params, pool, hb, searchdb).await -} - -#[get("/uiv1/search2")] -async fn handler_empty_query( - req: HttpRequest, - query_params: web::Query, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, - searchdb: web::Data>, -) -> Result { - handler2(req, "".to_string(), query_params, pool, hb, searchdb).await -} diff --git a/crates/bwmapserver/src/ssr/upload_map.rs b/crates/bwmapserver/src/ssr/upload_map.rs deleted file mode 100644 index f5f86cc..0000000 --- a/crates/bwmapserver/src/ssr/upload_map.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/upload-map")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - let langmap = if lang == bwcommon::LangData::Korean { - serde_json::json!({ - "h1": "업로드", - "h2": ".scm/.scx 파일 또는 맵 파일의 전체 디렉토리 업로드", - "h4_p1": "하나 이상의 .scm/.scx 파일을 업로드하려면 상단 파일 선택기를 선택하십시오. 전체 디렉터리와 해당 하위 디렉터리를 업로드하려면 맨 아래 파일 선택기를 선택합니다.", - "h4_p2": "또한 손상, 파손, 미완성, 테스트, 조작, 보호되지 않음, 복제 또는 - 오래된 지도. - 그만큼 - 웹사이트가 이 모든 것을 처리할 것이며 그 중 많은 부분이 스타크래프트 지도 제작 역사의 중요한 부분입니다. 조차 - 정확히 동일한 아카이브를 여러 번 업로드하는 것은 문제가 되지 않습니다. 그러니 가지고 있는 모든 것을 업로드하고 - 사이트에서 필터링 및 처리를 수행합니다. 전체 StarCraft 지도 디렉토리를 업로드해 보십시오: \\My Documents\\StarCraft", - "archive_label": "보관소", - "upload_button": "업로드", - "navbar": get_navbar_langmap(lang), - }) - } else { - serde_json::json!({ - "h1": "Upload", - "h2": "Upload .scm/.scx files or entire directories of map files", - "h4_p1": "If you want to upload one or more .scm/.scx files, then choose the top file picker. If you want to upload entire directories and their sub directories, then choose the bottom file picker.", - "h4_p2": "Also, please don't worry about corrupt, broken, unfinished, testing, rigged, unprotected, duplicate or - outdated maps. - The - website will handle all of this and many of them are important parts of StarCraft map making history. Even - uploading the exact same archive multiple times is no concern. So, please upload everything you have and let - the site do the filtering and processing. Try uploading your entire StarCraft map directory: \\My Documents\\StarCraft", - "archive_label": "Archive", - "upload_button": "Upload", - "navbar": get_navbar_langmap(lang), - }) - }; - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "upload-map", - &json!({ - "langmap": langmap, - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/upload_replay.rs b/crates/bwmapserver/src/ssr/upload_replay.rs deleted file mode 100644 index 3236aff..0000000 --- a/crates/bwmapserver/src/ssr/upload_replay.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/upload-replay")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "upload-replay", - &json!({ - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -} diff --git a/crates/bwmapserver/src/ssr/user.rs b/crates/bwmapserver/src/ssr/user.rs deleted file mode 100644 index 3981056..0000000 --- a/crates/bwmapserver/src/ssr/user.rs +++ /dev/null @@ -1,153 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpResponse, Responder}; -use actix_web::{HttpMessage, HttpRequest}; -use handlebars::{RenderError, RenderErrorReason}; -use serde_json::json; -use tracing::{info_span, instrument}; - -#[instrument(skip_all)] -#[get("/uiv1/user/{username}")] -async fn handler( - req: HttpRequest, - path: web::Path<(String,)>, - pool: web::Data< - bb8_postgres::bb8::Pool< - bb8_postgres::PostgresConnectionManager, - >, - >, - hb: web::Data>, -) -> Result { - let (username,) = path.into_inner(); - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct ReplayRow { - id: i64, - uploaded_time: i64, - denorm_scenario: String, - denorm_time_saved: i64, - denorm_frames: i64, - map_id: i64, - } - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct MapRow { - id: String, - uploaded_time: i64, - denorm_scenario: String, - } - - let user_id = { - let username = username.clone(); - pool.get() - .await? - .query_one( - " - select id - from account - where username = $1", - &[&username], - ) - .await? - .try_get::<_, i64>(0)? - }; - - let f1 = async { - let pool = pool.clone(); - let con = pool.get().await?; - let rows: Vec<_> = con.query(" - select replay.id, replay.uploaded_time, coalesce(map.denorm_scenario, ''), denorm_time_saved, denorm_frames, coalesce(map.id, -1) - from replay - full outer join map on map.chkblob = chkhash - where replay.uploaded_by = $1", &[&user_id]).await?.into_iter().map(|row| - { - ReplayRow { - id: row.get::<_, i64>(0), - uploaded_time: row.get::<_, i64>(1), - denorm_scenario: row.get::<_, String>(2), - denorm_time_saved: row.get::<_, i64>(3), - denorm_frames: row.get::<_, i64>(4), - map_id: row.get::<_, i64>(5), - } - }).collect(); - - Ok(rows) - }; - - let f2 = async { - let con = pool.get().await?; - let rows: Vec<_> = con.query(" - select map.id, uploaded_time, denorm_scenario - from map - where map.uploaded_by = $1 and outdated = false and broken = false and nsfw = false and unfinished = false and blackholed = false", &[&user_id]).await?.into_iter().map(|row| - { - MapRow { - id: bwcommon::get_web_id_from_db_id(row.try_get(0).unwrap(), crate::util::SEED_MAP_ID).unwrap(), - uploaded_time: row.get::<_, i64>(1), - denorm_scenario: row.get::<_, String>(2), - } - }).collect(); - - Ok(rows) - }; - - let f3 = async { - let con = pool.get().await?; - let rows: Vec<_> = con - .query( - " - select name - from playlist - where playlist.owner = $1", - &[&user_id], - ) - .await? - .into_iter() - .map(|row| anyhow::Ok(row.try_get::<_, String>("name")?)) - .collect::, _>>()?; - - anyhow::Ok(rows) - }; - - let (replays, maps, playlists) = futures::try_join!(f1, f2, f3)?; - - #[derive(Debug, serde::Serialize, serde::Deserialize)] - struct UserDump { - username: String, - maps: Vec, - replays: Vec, - } - - let ret = UserDump { - username, - maps, - replays, - }; - - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - let new_html = info_span!("render").in_scope(|| { - hb.render( - "user", - &json!({ - "user_dump": serde_json::to_string(&ret).map_err(|err| RenderError::from(RenderErrorReason::SerdeError(err)))?, - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - "playlists": playlists, - }), - ) - })?; - - Ok(HttpResponse::Ok().content_type("text/html").body(new_html)) -} diff --git a/crates/bwmapserver/src/ssr/viewer.rs b/crates/bwmapserver/src/ssr/viewer.rs deleted file mode 100644 index 44a6b87..0000000 --- a/crates/bwmapserver/src/ssr/viewer.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::middleware::UserSession; -use crate::ssr::get_navbar_langmap; -use actix_web::{get, web, HttpMessage, HttpResponse, Responder}; -use actix_web::{HttpRequest, Result}; -use serde_json::json; - -#[get("/uiv1/viewer")] -async fn handler( - req: HttpRequest, - hb: web::Data>, -) -> Result { - let lang = req - .extensions() - .get::() - .unwrap_or(&bwcommon::LangData::English) - .to_owned(); - - let user_username = req - .extensions() - .get::() - .map(|x| (x.username.clone(), true)) - .unwrap_or_default(); - - Ok(HttpResponse::Ok().content_type("text/html").body(hb.render( - "viewer", - &json!({ - "langmap": json!({ "navbar": get_navbar_langmap(lang) }), - "is_logged_in": user_username.1, - "username": user_username.0, - }), - )?)) -}