fix: app cache and other issues (#5460)
* fixes * #[serde(untagged)] my BEHATED (still kinda broken) * remove unused hasContent ref * clean up code in fetch instance * ping 3 times for average latency * fix: pinging to be more accurate TCP_NODELAY — Set on the TCP stream right after connect, preventing Nagle's algorithm from buffering the small ping packet (could save up to ~40ms) Instant over Utc::now() — Switched to monotonic std::time::Instant for timing, which is more precise and designed for measuring elapsed time (still using chrono just for the ping magic value) * delete useFetch util and just use native fetch * rename worlds until functions for more clarity * fix lint * fix cache.rs logic * make backend ping use both impls * Add optional timeout to server ping * fix gallery appearing in nav with no items * remove EU countries and add EU option for server country * add uk to europe --------- Co-authored-by: aecsocket <aecsocket@tutanota.com>
This commit is contained in:
185
Cargo.lock
generated
185
Cargo.lock
generated
@@ -2147,7 +2147,7 @@ dependencies = [
|
||||
"futures",
|
||||
"indexmap 2.11.4",
|
||||
"itertools 0.14.0",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"rust-s3",
|
||||
"serde",
|
||||
"serde-xml-rs",
|
||||
@@ -4189,9 +4189,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ico"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98"
|
||||
checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"png 0.17.16",
|
||||
@@ -4682,9 +4682,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.81"
|
||||
version = "0.3.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
|
||||
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@@ -4850,7 +4850,7 @@ dependencies = [
|
||||
"rand_chacha 0.3.1",
|
||||
"redis",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"rust-s3",
|
||||
"rust_decimal",
|
||||
"rust_iso3166",
|
||||
@@ -5305,7 +5305,7 @@ dependencies = [
|
||||
"log",
|
||||
"meilisearch-index-setting-macro",
|
||||
"pin-project-lite",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
@@ -5414,7 +5414,7 @@ dependencies = [
|
||||
"flate2",
|
||||
"maxminddb",
|
||||
"modrinth-util",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"tar",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -5493,7 +5493,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"derive_more 2.0.1",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"rust_decimal",
|
||||
"rust_iso3166",
|
||||
"secrecy",
|
||||
@@ -6247,7 +6247,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"http 1.3.1",
|
||||
"opentelemetry",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6262,7 +6262,7 @@ dependencies = [
|
||||
"opentelemetry-proto",
|
||||
"opentelemetry_sdk",
|
||||
"prost 0.13.5",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tonic 0.13.1",
|
||||
@@ -7703,11 +7703,45 @@ dependencies = [
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"wasm-streams 0.4.2",
|
||||
"web-sys",
|
||||
"webpki-roots 1.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.7.0",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams 0.5.0",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resolv-conf"
|
||||
version = "0.7.5"
|
||||
@@ -7893,7 +7927,7 @@ dependencies = [
|
||||
"minidom",
|
||||
"percent-encoding",
|
||||
"quick-xml 0.38.3",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -8361,7 +8395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48b85e25e8a1fc13928885e8bf13abe8a09e15c46993aed05d6405f7755d6e20"
|
||||
dependencies = [
|
||||
"httpdate",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"rustls 0.23.32",
|
||||
"sentry-backtrace",
|
||||
"sentry-contexts",
|
||||
@@ -9464,9 +9498,9 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.34.3"
|
||||
version = "0.34.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7"
|
||||
checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"block2 0.6.2",
|
||||
@@ -9538,9 +9572,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.8.5"
|
||||
version = "2.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c"
|
||||
checksum = "463ae8677aa6d0f063a900b9c41ecd4ac2b7ca82f0b058cc4491540e55b20129"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -9567,7 +9601,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"plist",
|
||||
"raw-window-handle",
|
||||
"reqwest",
|
||||
"reqwest 0.13.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
@@ -9582,7 +9616,6 @@ dependencies = [
|
||||
"tokio",
|
||||
"tray-icon",
|
||||
"url",
|
||||
"urlpattern",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
@@ -9591,9 +9624,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.4.1"
|
||||
version = "2.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f"
|
||||
checksum = "ca7bd893329425df750813e95bd2b643d5369d929438da96d5bbb7cc2c918f74"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -9615,9 +9648,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.4.0"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a"
|
||||
checksum = "aac423e5859d9f9ccdd32e3cf6a5866a15bedbf25aa6630bcb2acde9468f6ae3"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
@@ -9642,9 +9675,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.4.0"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e"
|
||||
checksum = "1b6a1bd2861ff0c8766b1d38b32a6a410f6dc6532d4ef534c47cfb2236092f59"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -9656,9 +9689,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.4.0"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f"
|
||||
checksum = "692a77abd8b8773e107a42ec0e05b767b8d2b7ece76ab36c6c3947e34df9f53f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glob",
|
||||
@@ -9712,9 +9745,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.4.2"
|
||||
version = "2.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "315784ec4be45e90a987687bae7235e6be3d6e9e350d2b75c16b8a4bf22c1db7"
|
||||
checksum = "ed390cc669f937afeb8b28032ce837bac8ea023d975a2e207375ec05afaf1804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
@@ -9734,16 +9767,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-http"
|
||||
version = "2.5.2"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "938a3d7051c9a82b431e3a0f3468f85715b3442b3c3a3913095e9fa509e2652c"
|
||||
checksum = "d8f069451c4e87e7e2636b7f065a4c52866c4ce5e60e2d53fa1038edb6d184dc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cookie_store",
|
||||
"data-url",
|
||||
"http 1.3.1",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -9827,7 +9860,7 @@ dependencies = [
|
||||
"minisign-verify",
|
||||
"osakit",
|
||||
"percent-encoding",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -9860,9 +9893,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.8.0"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846"
|
||||
checksum = "b885ffeac82b00f1f6fd292b6e5aabfa7435d537cef57d11e38a489956535651"
|
||||
dependencies = [
|
||||
"cookie 0.18.1",
|
||||
"dpi",
|
||||
@@ -9885,9 +9918,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.8.1"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807"
|
||||
checksum = "5204682391625e867d16584fedc83fc292fb998814c9f7918605c789cd876314"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http 1.3.1",
|
||||
@@ -9912,9 +9945,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.7.0"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212"
|
||||
checksum = "fcd169fccdff05eff2c1033210b9b94acd07a47e6fa9a3431cf09cfd4f01c87e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"brotli",
|
||||
@@ -10068,7 +10101,7 @@ dependencies = [
|
||||
"quick-xml 0.38.3",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest 0.12.24",
|
||||
"rgb",
|
||||
"serde",
|
||||
"serde_ini",
|
||||
@@ -10589,9 +10622,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.6"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bytes",
|
||||
@@ -11319,9 +11352,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.104"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
|
||||
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -11330,27 +11363,14 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.54"
|
||||
version = "0.4.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c"
|
||||
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@@ -11359,9 +11379,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.104"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
|
||||
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -11369,22 +11389,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.104"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
|
||||
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.104"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
|
||||
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -11402,6 +11422,19 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.11"
|
||||
@@ -11464,9 +11497,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.81"
|
||||
version = "0.3.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
|
||||
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -11484,9 +11517,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webkit2gtk"
|
||||
version = "2.0.1"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a"
|
||||
checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-rs",
|
||||
@@ -11508,9 +11541,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webkit2gtk-sys"
|
||||
version = "2.0.1"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c"
|
||||
checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cairo-sys-rs",
|
||||
@@ -12213,9 +12246,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.53.4"
|
||||
version = "0.54.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d78ec082b80fa088569a970d043bb3050abaabf4454101d44514ee8d9a8c9f6"
|
||||
checksum = "bb26159b420aa77684589a744ae9a9461a95395b848764ad12290a14d960a11a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"block2 0.6.2",
|
||||
|
||||
@@ -179,7 +179,7 @@ tauri = "2.8.5"
|
||||
tauri-build = "2.4.1"
|
||||
tauri-plugin-deep-link = "2.4.3"
|
||||
tauri-plugin-dialog = "2.4.0"
|
||||
tauri-plugin-http = "2.5.2"
|
||||
tauri-plugin-http = "2.5.7"
|
||||
tauri-plugin-opener = "2.5.0"
|
||||
tauri-plugin-os = "2.3.1"
|
||||
tauri-plugin-single-instance = "2.3.4"
|
||||
|
||||
@@ -85,7 +85,6 @@ import { debugAnalytics, initAnalytics, trackEvent } from '@/helpers/analytics'
|
||||
import { check_reachable } from '@/helpers/auth.js'
|
||||
import { get_user } from '@/helpers/cache.js'
|
||||
import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.ts'
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
|
||||
@@ -303,11 +302,7 @@ async function setupApp() {
|
||||
}),
|
||||
)
|
||||
|
||||
useFetch(
|
||||
`https://api.modrinth.com/appCriticalAnnouncement.json?version=${version}`,
|
||||
'criticalAnnouncements',
|
||||
true,
|
||||
)
|
||||
fetch(`https://api.modrinth.com/appCriticalAnnouncement.json?version=${version}`)
|
||||
.then((response) => response.json())
|
||||
.then((res) => {
|
||||
if (res && res.header && res.body) {
|
||||
@@ -320,23 +315,21 @@ async function setupApp() {
|
||||
)
|
||||
})
|
||||
|
||||
useFetch(`https://modrinth.com/news/feed/articles.json`, 'news', true)
|
||||
fetch(`https://modrinth.com/news/feed/articles.json`)
|
||||
.then((response) => response.json())
|
||||
.then((res) => {
|
||||
if (res && res.articles) {
|
||||
// Format expected by NewsArticleCard component.
|
||||
news.value = res.articles
|
||||
.map((article) => ({
|
||||
...article,
|
||||
path: article.link,
|
||||
thumbnail: article.thumbnail,
|
||||
title: article.title,
|
||||
summary: article.summary,
|
||||
date: article.date,
|
||||
}))
|
||||
.slice(0, 4)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to fetch news articles', error)
|
||||
})
|
||||
|
||||
get_opening_command().then(handleCommand)
|
||||
fetchCredentials()
|
||||
|
||||
@@ -33,6 +33,7 @@ async function purgeCache() {
|
||||
'user',
|
||||
'team',
|
||||
'organization',
|
||||
'file',
|
||||
'loader_manifest',
|
||||
'minecraft_manifest',
|
||||
'categories',
|
||||
@@ -40,8 +41,10 @@ async function purgeCache() {
|
||||
'loaders',
|
||||
'game_versions',
|
||||
'donation_platforms',
|
||||
'file_hash',
|
||||
'file_update',
|
||||
'search_results',
|
||||
'search_results_v3',
|
||||
]).catch(handleError)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
import { fetch } from '@tauri-apps/plugin-http'
|
||||
|
||||
export const useFetch = async (url, item, isSilent) => {
|
||||
try {
|
||||
const version = await getVersion()
|
||||
return await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: { 'User-Agent': `modrinth/theseus/${version} (support@modrinth.com)` },
|
||||
})
|
||||
} catch (err) {
|
||||
if (!isSilent) {
|
||||
throw err
|
||||
} else {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,6 +231,25 @@ export function isLinkedWorld(world: World): boolean {
|
||||
return world.type === 'server' && !!world.linked_project_id
|
||||
}
|
||||
|
||||
export async function getServerLatency(
|
||||
address: string,
|
||||
protocolVersion: ProtocolVersion | null = null,
|
||||
): Promise<number | undefined> {
|
||||
const pings: number[] = []
|
||||
for (let i = 0; i < 3; i++) {
|
||||
try {
|
||||
const status = await get_server_status(address, protocolVersion)
|
||||
if (status.ping != null) {
|
||||
pings.push(status.ping)
|
||||
}
|
||||
} catch {
|
||||
// Ignore individual ping failures
|
||||
}
|
||||
}
|
||||
if (pings.length === 0) return undefined
|
||||
return Math.round(pings.reduce((sum, p) => sum + p, 0) / pings.length)
|
||||
}
|
||||
|
||||
export async function refreshServerData(
|
||||
serverData: ServerData,
|
||||
protocolVersion: ProtocolVersion | null,
|
||||
|
||||
@@ -49,7 +49,7 @@ import {
|
||||
} from '@/helpers/profile.js'
|
||||
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
|
||||
import type { GameInstance } from '@/helpers/types'
|
||||
import { get_server_status } from '@/helpers/worlds'
|
||||
import { getServerLatency } from '@/helpers/worlds'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { getServerAddress, playServerProject, useInstall } from '@/store/install.js'
|
||||
|
||||
@@ -287,17 +287,18 @@ const {
|
||||
} = useServerSearch({ tags, query, maxResults, currentPage })
|
||||
|
||||
async function pingServerHits(hits: Labrinth.Search.v3.ResultSearchProject[]) {
|
||||
for (const hit of hits) {
|
||||
const address = hit.minecraft_java_server?.address
|
||||
if (!address) continue
|
||||
get_server_status(address)
|
||||
.then((status) => {
|
||||
serverPings.value = { ...serverPings.value, [hit.project_id]: status.ping }
|
||||
})
|
||||
.catch((err) => {
|
||||
const pingsToFetch = hits.filter((hit) => hit.minecraft_java_server?.address)
|
||||
await Promise.all(
|
||||
pingsToFetch.map(async (hit) => {
|
||||
const address = hit.minecraft_java_server!.address!
|
||||
try {
|
||||
const latency = await getServerLatency(address)
|
||||
serverPings.value = { ...serverPings.value, [hit.project_id]: latency }
|
||||
} catch (err) {
|
||||
console.error(`Failed to ping server ${address}:`, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const previousFilterState = ref('')
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<ServerPing v-if="ping" :ping="ping" />
|
||||
|
||||
<div
|
||||
v-if="modpackContentProjectV3 && (minecraftServer?.country || ping)"
|
||||
v-if="minecraftServer?.country || ping"
|
||||
class="w-1.5 h-1.5 rounded-full bg-surface-5"
|
||||
></div>
|
||||
|
||||
@@ -312,13 +312,13 @@ import InstanceSettingsModal from '@/components/ui/modal/InstanceSettingsModal.v
|
||||
import UpdateToPlayModal from '@/components/ui/modal/UpdateToPlayModal.vue'
|
||||
import NavTabs from '@/components/ui/NavTabs.vue'
|
||||
import { trackEvent } from '@/helpers/analytics'
|
||||
import { get_project_v3, get_version, get_version_many } from '@/helpers/cache.js'
|
||||
import { get_project_v3, get_version_many } from '@/helpers/cache.js'
|
||||
import { process_listener, profile_listener } from '@/helpers/events'
|
||||
import { get_by_profile_path } from '@/helpers/process'
|
||||
import { finish_install, get, get_full_path, get_projects, kill, run } from '@/helpers/profile'
|
||||
import { finish_install, get, get_full_path, kill, run } from '@/helpers/profile'
|
||||
import type { GameInstance } from '@/helpers/types'
|
||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||
import { get_server_status } from '@/helpers/worlds'
|
||||
import { get_server_status, getServerLatency } from '@/helpers/worlds'
|
||||
import { handleSevereError } from '@/store/error.js'
|
||||
import { playServerProject } from '@/store/install.js'
|
||||
import { useBreadcrumbs, useLoading } from '@/store/state'
|
||||
@@ -348,9 +348,7 @@ const exportModal = ref<InstanceType<typeof ExportModal>>()
|
||||
const updateToPlayModal = ref<InstanceType<typeof UpdateToPlayModal>>()
|
||||
|
||||
const isServerInstance = ref(false)
|
||||
const hasContent = ref(true)
|
||||
const linkedProjectV3 = ref<Labrinth.Projects.v3.Project>()
|
||||
const modpackContentProjectV3 = ref<Labrinth.Projects.v3.Project | null>(null)
|
||||
const selected = ref<unknown[]>([])
|
||||
|
||||
const minecraftServer = computed(() => linkedProjectV3.value?.minecraft_server)
|
||||
@@ -362,9 +360,7 @@ const ping = ref<number | undefined>(undefined)
|
||||
async function fetchInstance() {
|
||||
isServerInstance.value = false
|
||||
linkedProjectV3.value = undefined
|
||||
modpackContentProjectV3.value = null
|
||||
modrinthVersions.value = []
|
||||
hasContent.value = true
|
||||
ping.value = undefined
|
||||
|
||||
instance.value = await get(route.params.id as string).catch(handleError)
|
||||
@@ -382,48 +378,31 @@ async function fetchInstance() {
|
||||
(a: Labrinth.Versions.v2.Version, b: Labrinth.Versions.v2.Version) =>
|
||||
dayjs(b.date_published).valueOf() - dayjs(a.date_published).valueOf(),
|
||||
)
|
||||
if (linkedProjectV3.value?.minecraft_server != null) {
|
||||
isServerInstance.value = true
|
||||
}
|
||||
|
||||
const serverAddress = linkedProjectV3.value?.minecraft_java_server?.address
|
||||
if (serverAddress) {
|
||||
get_server_status(serverAddress)
|
||||
.then((status) => {
|
||||
if (status.ping != null) {
|
||||
ping.value = status.ping
|
||||
playersOnline.value = status.players?.online
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Failed to ping server ${serverAddress}:`, err)
|
||||
})
|
||||
if (linkedProjectV3.value?.minecraft_server != null) {
|
||||
isServerInstance.value = true
|
||||
|
||||
const serverAddress = linkedProjectV3.value?.minecraft_java_server?.address
|
||||
if (serverAddress) {
|
||||
try {
|
||||
const status = await get_server_status(serverAddress)
|
||||
const latency = await getServerLatency(serverAddress)
|
||||
ping.value = latency
|
||||
playersOnline.value = status.players?.online
|
||||
} catch (err) {
|
||||
console.error(`Failed to ping server ${serverAddress}:`, err)
|
||||
}
|
||||
|
||||
await fetchModpackContent()
|
||||
const projects = await get_projects(instance.value!.path).catch(() => ({}))
|
||||
hasContent.value = Object.keys(projects).length > 0
|
||||
}
|
||||
}
|
||||
} catch (error: Error) {
|
||||
handleError(error)
|
||||
} catch (error) {
|
||||
handleError(error as Error)
|
||||
}
|
||||
}
|
||||
|
||||
await updatePlayState()
|
||||
}
|
||||
|
||||
async function fetchModpackContent() {
|
||||
modpackContentProjectV3.value = null
|
||||
const versionId = instance.value?.linked_data?.version_id
|
||||
if (!versionId) return
|
||||
|
||||
const contentVersion = await get_version(versionId, 'must_revalidate')
|
||||
const projectId = contentVersion?.project_id
|
||||
if (projectId) {
|
||||
modpackContentProjectV3.value = await get_project_v3(projectId, 'must_revalidate')
|
||||
}
|
||||
}
|
||||
|
||||
async function updatePlayState() {
|
||||
const runningProcesses = await get_by_profile_path(route.params.id as string).catch(handleError)
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ import {
|
||||
list as listInstances,
|
||||
} from '@/helpers/profile'
|
||||
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
|
||||
import { get_server_status } from '@/helpers/worlds'
|
||||
import { getServerLatency } from '@/helpers/worlds'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import {
|
||||
getServerAddress,
|
||||
@@ -398,15 +398,11 @@ async function fetchProjectData() {
|
||||
serverStatusOnline.value = !!projectV3.value?.minecraft_java_server?.ping?.data
|
||||
if (serverAddress) {
|
||||
serverPing.value = undefined
|
||||
get_server_status(serverAddress)
|
||||
.then((status) => {
|
||||
if (status.ping != null) {
|
||||
serverPing.value = status.ping
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Failed to ping server ${serverAddress}:`, err)
|
||||
})
|
||||
try {
|
||||
serverPing.value = await getServerLatency(serverAddress)
|
||||
} catch (error) {
|
||||
console.error(`Failed to ping server ${serverAddress}:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch server sidebar data (modpack version + project)
|
||||
|
||||
@@ -328,7 +328,7 @@ export const installServerProject = async (serverProjectId) => {
|
||||
})
|
||||
await edit_icon(profilePath, originalIconPath)
|
||||
|
||||
await syncServerAsWorld(profilePath, project.title, serverAddress, serverProjectId)
|
||||
await syncServerProjectAsWorld(profilePath, project.title, serverAddress, serverProjectId)
|
||||
}
|
||||
|
||||
export const getServerAddress = (javaServer) => {
|
||||
@@ -337,7 +337,7 @@ export const getServerAddress = (javaServer) => {
|
||||
return port !== 25565 ? `${address}:${port}` : address
|
||||
}
|
||||
|
||||
const syncServerAsWorld = async (
|
||||
const syncServerProjectAsWorld = async (
|
||||
profilePath,
|
||||
serverName,
|
||||
serverAddress,
|
||||
@@ -405,7 +405,7 @@ const findInstalledInstance = async (projectId) => {
|
||||
return packs.find((pack) => pack.linked_data?.project_id === projectId) ?? null
|
||||
}
|
||||
|
||||
const createVanillaInstance = async (project, gameVersion, serverAddress) => {
|
||||
const createVanillaServerInstance = async (project, gameVersion, serverAddress) => {
|
||||
const profilePath = await create(
|
||||
project.title,
|
||||
gameVersion,
|
||||
@@ -420,7 +420,8 @@ const createVanillaInstance = async (project, gameVersion, serverAddress) => {
|
||||
},
|
||||
)
|
||||
|
||||
await syncServerAsWorld(profilePath, project.title, serverAddress, project.id)
|
||||
//
|
||||
await syncServerProjectAsWorld(profilePath, project.title, serverAddress, project.id)
|
||||
|
||||
return profilePath
|
||||
}
|
||||
@@ -514,6 +515,7 @@ export const playServerProject = async (projectId) => {
|
||||
|
||||
if (projectV3?.minecraft_server == null) {
|
||||
console.warn('playServerProject failed: project is not a server project')
|
||||
return
|
||||
}
|
||||
|
||||
const content = projectV3?.minecraft_java_server?.content
|
||||
@@ -529,7 +531,7 @@ export const playServerProject = async (projectId) => {
|
||||
if (installStore.installingServerProjects.includes(projectId)) return
|
||||
installStore.startInstallingServer(projectId)
|
||||
try {
|
||||
const path = await createVanillaInstance(project, recommendedGameVersion, serverAddress)
|
||||
const path = await createVanillaServerInstance(project, recommendedGameVersion, serverAddress)
|
||||
if (path) {
|
||||
instance = await get(path)
|
||||
showModpackInstallSuccess(installStore, instance, serverAddress)
|
||||
@@ -543,8 +545,6 @@ export const playServerProject = async (projectId) => {
|
||||
installStore.showInstallToPlayModal(projectV3, modpackVersionId, async () => {
|
||||
const newInstance = await findInstalledInstance(project.id)
|
||||
if (!newInstance) return
|
||||
// Ensure the server is in the worlds list after modpack install
|
||||
await syncServerAsWorld(newInstance.path, project.title, serverAddress, project.id)
|
||||
showModpackInstallSuccess(installStore, newInstance, serverAddress)
|
||||
})
|
||||
return
|
||||
@@ -552,7 +552,7 @@ export const playServerProject = async (projectId) => {
|
||||
|
||||
if (!instance) return
|
||||
|
||||
await syncServerAsWorld(instance.path, project.title, serverAddress, project.id)
|
||||
await syncServerProjectAsWorld(instance.path, project.title, serverAddress, project.id)
|
||||
|
||||
// Update existing instance if needed
|
||||
if (isModpack && instance.linked_data?.version_id !== modpackVersionId) {
|
||||
|
||||
@@ -2523,6 +2523,13 @@ const navLinks = computed(() => {
|
||||
const routeType = route.params.type || project.value.project_type
|
||||
const projectUrl = `/${routeType}/${project.value.slug ? project.value.slug : project.value.id}`
|
||||
|
||||
const galleryCount =
|
||||
routeType === 'server'
|
||||
? project.value.gallery.filter((item) => item.name === '__mc_server_banner__').length
|
||||
: project.value.gallery.length
|
||||
|
||||
console.log('galleryCount', galleryCount, !!currentMember.value)
|
||||
|
||||
return [
|
||||
{
|
||||
label: formatMessage(messages.descriptionTab),
|
||||
@@ -2531,7 +2538,7 @@ const navLinks = computed(() => {
|
||||
{
|
||||
label: formatMessage(messages.galleryTab),
|
||||
href: `${projectUrl}/gallery`,
|
||||
shown: project.value.gallery.length > 0 || !!currentMember.value,
|
||||
shown: galleryCount > 0 || !!currentMember.value,
|
||||
},
|
||||
{
|
||||
label: formatMessage(messages.changelogTab),
|
||||
|
||||
@@ -281,35 +281,46 @@ if (projectV3.value) {
|
||||
const countryOptions = [
|
||||
{ value: 'US', label: 'United States' },
|
||||
{ value: 'CA', label: 'Canada' },
|
||||
{ value: 'GB', label: 'United Kingdom' },
|
||||
{ value: 'DE', label: 'Germany' },
|
||||
{ value: 'FR', label: 'France' },
|
||||
{ value: 'NL', label: 'Netherlands' },
|
||||
{ value: 'FI', label: 'Finland' },
|
||||
{ value: 'SE', label: 'Sweden' },
|
||||
{
|
||||
value: 'EU',
|
||||
label: 'Europe',
|
||||
searchTerms: [
|
||||
'Germany',
|
||||
'France',
|
||||
'Netherlands',
|
||||
'Finland',
|
||||
'Sweden',
|
||||
'Denmark',
|
||||
'Poland',
|
||||
'Czech Republic',
|
||||
'Romania',
|
||||
'Austria',
|
||||
'Belgium',
|
||||
'Ireland',
|
||||
'Spain',
|
||||
'Italy',
|
||||
'Portugal',
|
||||
'Lithuania',
|
||||
'Latvia',
|
||||
'Estonia',
|
||||
'Bulgaria',
|
||||
'Croatia',
|
||||
'Hungary',
|
||||
'Slovakia',
|
||||
'Greece',
|
||||
'Luxembourg',
|
||||
'Malta',
|
||||
'Cyprus',
|
||||
'Slovenia',
|
||||
'Great Britain',
|
||||
'United Kingdom',
|
||||
],
|
||||
},
|
||||
{ value: 'NO', label: 'Norway' },
|
||||
{ value: 'DK', label: 'Denmark' },
|
||||
{ value: 'PL', label: 'Poland' },
|
||||
{ value: 'CZ', label: 'Czech Republic' },
|
||||
{ value: 'RO', label: 'Romania' },
|
||||
{ value: 'CH', label: 'Switzerland' },
|
||||
{ value: 'AT', label: 'Austria' },
|
||||
{ value: 'BE', label: 'Belgium' },
|
||||
{ value: 'IE', label: 'Ireland' },
|
||||
{ value: 'ES', label: 'Spain' },
|
||||
{ value: 'IT', label: 'Italy' },
|
||||
{ value: 'PT', label: 'Portugal' },
|
||||
{ value: 'RU', label: 'Russia' },
|
||||
{ value: 'UA', label: 'Ukraine' },
|
||||
{ value: 'LT', label: 'Lithuania' },
|
||||
{ value: 'LV', label: 'Latvia' },
|
||||
{ value: 'EE', label: 'Estonia' },
|
||||
{ value: 'BG', label: 'Bulgaria' },
|
||||
{ value: 'HR', label: 'Croatia' },
|
||||
{ value: 'HU', label: 'Hungary' },
|
||||
{ value: 'SK', label: 'Slovakia' },
|
||||
{ value: 'RS', label: 'Serbia' },
|
||||
{ value: 'GR', label: 'Greece' },
|
||||
{ value: 'TR', label: 'Turkey' },
|
||||
{ value: 'IL', label: 'Israel' },
|
||||
{ value: 'AE', label: 'United Arab Emirates' },
|
||||
|
||||
@@ -54,7 +54,7 @@ impl ServerPingQueue {
|
||||
|
||||
let mut retries = ENV.SERVER_PING_RETRIES;
|
||||
let result = loop {
|
||||
match ping_server(&address, port).await {
|
||||
match ping_server(&address, port, None).await {
|
||||
Ok(ping) => {
|
||||
info!(?ping, "Received successful ping");
|
||||
break Ok(ping);
|
||||
@@ -252,39 +252,15 @@ impl ServerPingQueue {
|
||||
pub async fn ping_server(
|
||||
address: &str,
|
||||
port: u16,
|
||||
timeout: Option<Duration>,
|
||||
) -> eyre::Result<exp::minecraft::JavaServerPingData> {
|
||||
let start = Instant::now();
|
||||
let timeout = Duration::from_millis(ENV.SERVER_PING_TIMEOUT_MS);
|
||||
let default_duration = Duration::from_millis(ENV.SERVER_PING_TIMEOUT_MS);
|
||||
let timeout = timeout
|
||||
.map(|duration| duration.min(default_duration))
|
||||
.unwrap_or(default_duration);
|
||||
|
||||
let task1 = async move {
|
||||
let conn = async_minecraft_ping::ConnectionConfig::build(address)
|
||||
.with_port(port)
|
||||
.connect()
|
||||
.await
|
||||
.wrap_err("failed to connect to server")?;
|
||||
|
||||
let status = conn
|
||||
.status()
|
||||
.await
|
||||
.wrap_err("failed to get server status")?
|
||||
.status;
|
||||
|
||||
debug!("Successful ping with `async_minecraft_ping`");
|
||||
eyre::Ok(exp::minecraft::JavaServerPingData {
|
||||
latency: start.elapsed(),
|
||||
version_name: status.version.name,
|
||||
version_protocol: status.version.protocol,
|
||||
description: match status.description {
|
||||
ServerDescription::Plain(text)
|
||||
| ServerDescription::Object { text } => text,
|
||||
},
|
||||
players_online: status.players.online,
|
||||
players_max: status.players.max,
|
||||
})
|
||||
};
|
||||
let task1 = tokio::time::timeout(timeout, task1);
|
||||
|
||||
let task2 = async move {
|
||||
let task_ep = async move {
|
||||
fn map_component(c: elytra_ping::parse::TextComponent) -> String {
|
||||
match c {
|
||||
elytra_ping::parse::TextComponent::Plain(t) => t,
|
||||
@@ -303,7 +279,6 @@ pub async fn ping_server(
|
||||
elytra_ping::ping_or_timeout((address.to_string(), port), timeout)
|
||||
.await?;
|
||||
|
||||
debug!("Successful ping with `elytra_ping`");
|
||||
eyre::Ok(exp::minecraft::JavaServerPingData {
|
||||
latency,
|
||||
version_name: result
|
||||
@@ -326,15 +301,54 @@ pub async fn ping_server(
|
||||
})
|
||||
};
|
||||
|
||||
async move {
|
||||
if let Ok(t) = task1
|
||||
.await
|
||||
.wrap_err("failed to ping with `async_minecraft_ping`")?
|
||||
{
|
||||
return Ok(t);
|
||||
}
|
||||
let task_amp = async move {
|
||||
let task = async move {
|
||||
let conn = async_minecraft_ping::ConnectionConfig::build(address)
|
||||
.with_port(port)
|
||||
.connect()
|
||||
.await
|
||||
.wrap_err("failed to connect to server")?;
|
||||
|
||||
task2.await.wrap_err("failed to ping with `elytra_ping`")
|
||||
let status = conn
|
||||
.status()
|
||||
.await
|
||||
.wrap_err("failed to get server status")?
|
||||
.status;
|
||||
|
||||
eyre::Ok(exp::minecraft::JavaServerPingData {
|
||||
latency: start.elapsed(),
|
||||
version_name: status.version.name,
|
||||
version_protocol: status.version.protocol,
|
||||
description: match status.description {
|
||||
ServerDescription::Plain(text)
|
||||
| ServerDescription::Object { text } => text,
|
||||
},
|
||||
players_online: status.players.online,
|
||||
players_max: status.players.max,
|
||||
})
|
||||
};
|
||||
|
||||
tokio::time::timeout(timeout, task)
|
||||
.await
|
||||
.map_err(eyre::Error::new)
|
||||
.flatten()
|
||||
};
|
||||
|
||||
async move {
|
||||
let (result_ep, result_amp) = (task_ep.await, task_amp.await);
|
||||
|
||||
let result_ep = result_ep
|
||||
.inspect(|_| debug!("Successful ping with `elytra_ping`"))
|
||||
.inspect_err(|err| {
|
||||
debug!("Failed to ping with `elytra_ping`: {err:#}")
|
||||
});
|
||||
let result_amp = result_amp
|
||||
.inspect(|_| debug!("Successful ping with `async_minecraft_ping`"))
|
||||
.inspect_err(|err| {
|
||||
debug!("Failed to ping with `async_minecraft_ping`: {err:#}")
|
||||
});
|
||||
|
||||
result_ep.or(result_amp)
|
||||
}
|
||||
.await
|
||||
}
|
||||
@@ -359,11 +373,20 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_ping_server_success() {
|
||||
let _status = ping_server("mc.hypixel.net", 25565).await.unwrap();
|
||||
let _status = ping_server("mc.hypixel.net", 25565, None).await.unwrap();
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_ping_server_invalid_address() {
|
||||
_ = ping_server("invalid.invalid", 25565).await.unwrap_err();
|
||||
_ = ping_server("invalid.invalid", 25565, None)
|
||||
.await
|
||||
.unwrap_err();
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_ping_zero_timeout() {
|
||||
_ = ping_server("hypixel.net", 25565, Some(Duration::ZERO))
|
||||
.await
|
||||
.unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use actix_web::{HttpRequest, post, web};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -18,6 +20,7 @@ pub fn config(cfg: &mut utoipa_actix_web::service_config::ServiceConfig) {
|
||||
pub struct PingRequest {
|
||||
pub address: String,
|
||||
pub port: u16,
|
||||
pub timeout_ms: Option<u64>,
|
||||
}
|
||||
|
||||
#[utoipa::path]
|
||||
@@ -38,7 +41,8 @@ pub async fn ping_minecraft_java(
|
||||
)
|
||||
.await?;
|
||||
|
||||
server_ping::ping_server(&request.address, request.port)
|
||||
let timeout = request.timeout_ms.map(Duration::from_millis);
|
||||
server_ping::ping_server(&request.address, request.port, timeout)
|
||||
.await
|
||||
.wrap_request_err("failed to ping server")?;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use std::path::{Path, PathBuf};
|
||||
// 1 day
|
||||
const DEFAULT_ID: &str = "0";
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CacheValueType {
|
||||
Project,
|
||||
@@ -131,37 +131,41 @@ impl CacheValueType {
|
||||
}
|
||||
}
|
||||
|
||||
// De/serialization strategy:
|
||||
// - on serialize:
|
||||
// - in the `cache` table, save the `data_type` (variant of this value) alongside
|
||||
// the data
|
||||
// - data column contains the serialized form of the INNER value (i.e. for a
|
||||
// `CacheValue::Project`, we serialize it as a `Project,` NOT as a `CacheValue`)
|
||||
// - this way, we do not tag the data using serde in any way
|
||||
// - on deserialize:
|
||||
// - use the `data_type` to figure out what type of value to deser as
|
||||
// - then wrap that in a `CacheValue`
|
||||
//
|
||||
// do NOT use `#[serde(untagged)]` here, since then a value of one variant can be
|
||||
// deser'd as a value of another variant, if it comes before it in the enum
|
||||
// definition list.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum CacheValue {
|
||||
Project(Project),
|
||||
|
||||
ProjectV3(ProjectV3),
|
||||
|
||||
Version(Version),
|
||||
|
||||
User(User),
|
||||
|
||||
Team(Vec<TeamMember>),
|
||||
|
||||
Organization(Organization),
|
||||
|
||||
File(CachedFile),
|
||||
|
||||
LoaderManifest(CachedLoaderManifest),
|
||||
MinecraftManifest(daedalus::minecraft::VersionManifest),
|
||||
|
||||
Categories(Vec<Category>),
|
||||
ReportTypes(Vec<String>),
|
||||
Loaders(Vec<Loader>),
|
||||
GameVersions(Vec<GameVersion>),
|
||||
DonationPlatforms(Vec<DonationPlatform>),
|
||||
|
||||
FileHash(CachedFileHash),
|
||||
FileUpdate(CachedFileUpdate),
|
||||
SearchResults(SearchResults),
|
||||
SearchResultsV3(SearchResultsV3),
|
||||
ProjectV3(ProjectV3),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -566,6 +570,47 @@ impl CacheValue {
|
||||
| CacheValue::SearchResultsV3(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_json_value(&self) -> crate::Result<serde_json::Value> {
|
||||
let value = match self {
|
||||
CacheValue::Project(project) => serde_json::to_value(project),
|
||||
CacheValue::ProjectV3(project) => serde_json::to_value(project),
|
||||
CacheValue::Version(version) => serde_json::to_value(version),
|
||||
CacheValue::User(user) => serde_json::to_value(user),
|
||||
CacheValue::Team(members) => serde_json::to_value(members),
|
||||
CacheValue::Organization(org) => serde_json::to_value(org),
|
||||
CacheValue::File(file) => serde_json::to_value(file),
|
||||
CacheValue::LoaderManifest(loader) => serde_json::to_value(loader),
|
||||
CacheValue::MinecraftManifest(manifest) => {
|
||||
serde_json::to_value(manifest)
|
||||
}
|
||||
CacheValue::Categories(categories) => {
|
||||
serde_json::to_value(categories)
|
||||
}
|
||||
CacheValue::ReportTypes(report_types) => {
|
||||
serde_json::to_value(report_types)
|
||||
}
|
||||
CacheValue::Loaders(loaders) => serde_json::to_value(loaders),
|
||||
CacheValue::GameVersions(versions) => {
|
||||
serde_json::to_value(versions)
|
||||
}
|
||||
CacheValue::DonationPlatforms(platforms) => {
|
||||
serde_json::to_value(platforms)
|
||||
}
|
||||
CacheValue::FileHash(hash) => serde_json::to_value(hash),
|
||||
CacheValue::FileUpdate(update) => serde_json::to_value(update),
|
||||
CacheValue::SearchResults(search) => serde_json::to_value(search),
|
||||
CacheValue::SearchResultsV3(search) => serde_json::to_value(search),
|
||||
}
|
||||
.map_err(|err| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Failed to serialize cache value: {err}"
|
||||
))
|
||||
.as_error()
|
||||
})?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@@ -759,15 +804,11 @@ impl CachedEntry {
|
||||
.await?;
|
||||
|
||||
for row in query {
|
||||
let row_exists = row.data.is_some();
|
||||
let parsed_data = row
|
||||
.data
|
||||
.and_then(|x| serde_json::from_value::<CacheValue>(x).ok());
|
||||
|
||||
// If data is corrupted/failed to parse ignore it
|
||||
if row_exists && parsed_data.is_none() {
|
||||
continue;
|
||||
}
|
||||
let parsed_data = if let Some(data) = row.data.clone() {
|
||||
Some(Self::deserialize_cache_value(type_, data, &row.id)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if row.expires <= Utc::now().timestamp() {
|
||||
if cache_behaviour == CacheBehaviour::MustRevalidate {
|
||||
@@ -789,6 +830,16 @@ impl CachedEntry {
|
||||
});
|
||||
|
||||
if let Some(data) = parsed_data {
|
||||
if data.get_type() != type_ {
|
||||
return Err(crate::ErrorKind::OtherError(format!(
|
||||
"Cache type mismatch for id {}: expected {:?}, got {:?}",
|
||||
row.id,
|
||||
type_,
|
||||
data.get_type()
|
||||
))
|
||||
.as_error());
|
||||
}
|
||||
|
||||
return_vals.push(Self {
|
||||
id: row.id,
|
||||
alias: row.alias,
|
||||
@@ -1509,11 +1560,102 @@ impl CachedEntry {
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_cache_value(
|
||||
type_: CacheValueType,
|
||||
data: serde_json::Value,
|
||||
id: &str,
|
||||
) -> crate::Result<CacheValue> {
|
||||
fn parse<T: DeserializeOwned>(
|
||||
data: serde_json::Value,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> crate::Result<T> {
|
||||
serde_json::from_value::<T>(data.clone()).map_err(|err| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Failed to deserialize cache {label} for id {id}: {err}\n\ndata:\n{}",
|
||||
serde_json::to_string_pretty(&data).unwrap(),
|
||||
))
|
||||
.as_error()
|
||||
})
|
||||
}
|
||||
|
||||
let value = match type_ {
|
||||
CacheValueType::Project => {
|
||||
CacheValue::Project(parse(data, id, "project")?)
|
||||
}
|
||||
CacheValueType::ProjectV3 => {
|
||||
CacheValue::ProjectV3(parse(data, id, "project_v3")?)
|
||||
}
|
||||
CacheValueType::Version => {
|
||||
CacheValue::Version(parse(data, id, "version")?)
|
||||
}
|
||||
CacheValueType::User => CacheValue::User(parse(data, id, "user")?),
|
||||
CacheValueType::Team => CacheValue::Team(parse(data, id, "team")?),
|
||||
CacheValueType::Organization => {
|
||||
CacheValue::Organization(parse(data, id, "organization")?)
|
||||
}
|
||||
CacheValueType::File => CacheValue::File(parse(data, id, "file")?),
|
||||
CacheValueType::LoaderManifest => {
|
||||
CacheValue::LoaderManifest(parse(data, id, "loader_manifest")?)
|
||||
}
|
||||
CacheValueType::MinecraftManifest => CacheValue::MinecraftManifest(
|
||||
parse(data, id, "minecraft_manifest")?,
|
||||
),
|
||||
CacheValueType::Categories => {
|
||||
CacheValue::Categories(parse(data, id, "categories")?)
|
||||
}
|
||||
CacheValueType::ReportTypes => {
|
||||
CacheValue::ReportTypes(parse(data, id, "report_types")?)
|
||||
}
|
||||
CacheValueType::Loaders => {
|
||||
CacheValue::Loaders(parse(data, id, "loaders")?)
|
||||
}
|
||||
CacheValueType::GameVersions => {
|
||||
CacheValue::GameVersions(parse(data, id, "game_versions")?)
|
||||
}
|
||||
CacheValueType::DonationPlatforms => CacheValue::DonationPlatforms(
|
||||
parse(data, id, "donation_platforms")?,
|
||||
),
|
||||
CacheValueType::FileHash => {
|
||||
CacheValue::FileHash(parse(data, id, "file_hash")?)
|
||||
}
|
||||
CacheValueType::FileUpdate => {
|
||||
CacheValue::FileUpdate(parse(data, id, "file_update")?)
|
||||
}
|
||||
CacheValueType::SearchResults => {
|
||||
CacheValue::SearchResults(parse(data, id, "search_results")?)
|
||||
}
|
||||
CacheValueType::SearchResultsV3 => CacheValue::SearchResultsV3(
|
||||
parse(data, id, "search_results_v3")?,
|
||||
),
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub(crate) async fn upsert_many(
|
||||
items: &[Self],
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
) -> crate::Result<()> {
|
||||
let items = serde_json::to_string(items)?;
|
||||
let items = items
|
||||
.iter()
|
||||
.map(|item| {
|
||||
let data = item
|
||||
.data
|
||||
.as_ref()
|
||||
.map(|value| value.to_json_value())
|
||||
.transpose()?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"id": item.id,
|
||||
"data_type": item.type_.as_str(),
|
||||
"alias": item.alias,
|
||||
"data": data,
|
||||
"expires": item.expires,
|
||||
}))
|
||||
})
|
||||
.collect::<crate::Result<Vec<_>>>()?;
|
||||
let items = serde_json::to_string(&items)?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
|
||||
@@ -69,7 +69,7 @@ pub async fn get_server_status(
|
||||
mod modern {
|
||||
use super::ServerStatus;
|
||||
use crate::ErrorKind;
|
||||
use chrono::Utc;
|
||||
use std::time::Instant;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpStream, ToSocketAddrs};
|
||||
|
||||
@@ -79,6 +79,7 @@ mod modern {
|
||||
protocol_version: Option<u32>,
|
||||
) -> crate::Result<ServerStatus> {
|
||||
let mut stream = TcpStream::connect(address).await?;
|
||||
stream.set_nodelay(true)?;
|
||||
handshake(&mut stream, original_address, protocol_version).await?;
|
||||
let mut result = status_body(&mut stream).await?;
|
||||
result.ping = ping(&mut stream).await.ok();
|
||||
@@ -155,9 +156,9 @@ mod modern {
|
||||
}
|
||||
|
||||
async fn ping(stream: &mut TcpStream) -> crate::Result<i64> {
|
||||
let start_time = Utc::now();
|
||||
let ping_magic = start_time.timestamp_millis();
|
||||
let ping_magic = chrono::Utc::now().timestamp_millis();
|
||||
|
||||
let start_time = Instant::now();
|
||||
stream.write_all(&[0x09, 0x01]).await?;
|
||||
stream.write_i64(ping_magic).await?;
|
||||
stream.flush().await?;
|
||||
@@ -172,8 +173,7 @@ mod modern {
|
||||
.into());
|
||||
}
|
||||
|
||||
let response_time = Utc::now();
|
||||
Ok((response_time - start_time).num_milliseconds())
|
||||
Ok(start_time.elapsed().as_millis() as i64)
|
||||
}
|
||||
|
||||
mod varint {
|
||||
|
||||
@@ -156,6 +156,7 @@ export interface ComboboxOption<T> {
|
||||
href?: string
|
||||
target?: string
|
||||
action?: () => void
|
||||
searchTerms?: string[]
|
||||
}
|
||||
|
||||
const DROPDOWN_VIEWPORT_MARGIN = 8
|
||||
@@ -272,7 +273,9 @@ const filteredOptions = computed(() => {
|
||||
const query = searchQuery.value.toLowerCase()
|
||||
return optionsWithKeys.value.filter((opt) => {
|
||||
if (isDivider(opt)) return false
|
||||
return opt.label.toLowerCase().includes(query)
|
||||
if (opt.label.toLowerCase().includes(query)) return true
|
||||
if (opt.searchTerms?.some((term) => term.toLowerCase().includes(query))) return true
|
||||
return false
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user