1
0

Inherit dependencies from workspace manifest, and optimize some out (#3655)

* chore: inherit dependencies from workspace, optimize some deps out

* Update bitflags from 2.9.0 to 2.9.1

* Fix temp directory leak in check_java_at_filepath

* Fix build

* Fix lint

* chore(app-lib): refactor overkill `futures` executor usage to Tokio MPSC

* chore: fix Clippy lint

* tweak: optimize out dependency on OpenSSL source build

Contrary to what I expected before, this was caused due to the Tauri
updater plugin using a different TLS stack than everything else.

* chore(labrinth): drop now unused dependency

* Update zip because 2.6.1 got yanked

* Downgrade weezl to 0.1.8

* Mention that p256 is also a blocker for rand 0.9

* chore: sidestep GitHub review requirements

* chore: sidestep GitHub review requirements (2)

* chore: sidestep GitHub review requirements (3)

---------

Co-authored-by: Josiah Glosson <soujournme@gmail.com>
This commit is contained in:
Alejandro González
2025-05-15 22:47:29 +02:00
committed by GitHub
parent 37cc81a36d
commit f19643095e
35 changed files with 876 additions and 1020 deletions

798
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +1,182 @@
[workspace]
resolver = '2'
resolver = "2"
members = [
'./packages/app-lib',
'./apps/app-playground',
'./apps/app',
'./apps/labrinth',
'./apps/daedalus_client',
'./packages/daedalus',
'./packages/ariadne',
"apps/app",
"apps/app-playground",
"apps/daedalus_client",
"apps/labrinth",
"packages/app-lib",
"packages/ariadne",
"packages/daedalus",
]
# Optimize for speed and reduce size on release builds
[profile.release]
panic = "abort" # Strip expensive panic clean-up logic
codegen-units = 1 # Compile crates one after another so the compiler can optimize better
lto = true # Enables link to optimizations
opt-level = "s" # Optimize for binary size
strip = true # Remove debug symbols
[profile.dev.package.sqlx-macros]
opt-level = 3
[workspace.dependencies]
actix-cors = "0.7.1"
actix-files = "0.6.6"
actix-http = "3.11.0"
actix-multipart = "0.7.2"
actix-rt = "2.10.0"
actix-web = "4.11.0"
actix-web-prom = "0.10.0"
actix-ws = "0.3.0"
argon2 = { version = "0.5.3", features = ["std"] }
ariadne = { path = "packages/ariadne" }
async-compression = { version = "0.4.23", default-features = false }
async-recursion = "1.1.1"
async-stripe = { version = "0.41.0", default-features = false, features = [
"runtime-tokio-hyper-rustls",
] }
async-trait = "0.1.88"
async-tungstenite = { version = "0.29.1", default-features = false, features = [
"futures-03-sink",
] }
async-walkdir = "2.1.0"
async_zip = "0.0.17"
base64 = "0.22.1"
bitflags = "2.9.0"
bytes = "1.10.1"
censor = "0.3.0"
chrono = "0.4.41"
clap = "4.5.38"
clickhouse = "0.13.2"
color-thief = "0.2.2"
console-subscriber = "0.4.1"
daedalus = { path = "packages/daedalus" }
dashmap = "6.1.0"
deadpool-redis = "0.20.0"
dirs = "6.0.0"
discord-rich-presence = "0.2.5"
dotenv-build = "0.1.1"
dotenvy = "0.15.7"
dunce = "1.0.5"
either = "1.15.0"
enumset = "1.1.6"
flate2 = "1.1.1"
fs4 = { version = "0.13.1", default-features = false }
futures = { version = "0.3.31", default-features = false }
futures-util = "0.3.31"
hex = "0.4.3"
hickory-resolver = "0.25.2"
hmac = "0.12.1"
hyper-tls = "0.6.0"
hyper-util = "0.1.11"
iana-time-zone = "0.1.63"
image = { version = "0.25.6", default-features = false, features = ["rayon"] }
indexmap = "2.9.0"
indicatif = "0.17.11"
itertools = "0.14.0"
jemalloc_pprof = "0.7.0"
json-patch = { version = "4.0.0", default-features = false }
lettre = { version = "0.11.16", default-features = false, features = [
"builder",
"hostname",
"pool",
"ring",
"rustls",
"rustls-native-certs",
"smtp-transport",
] }
maxminddb = "0.26.0"
meilisearch-sdk = { version = "0.28.0", default-features = false }
murmur2 = "0.1.0"
native-dialog = "0.9.0"
notify = { version = "8.0.0", default-features = false }
notify-debouncer-mini = { version = "0.6.0", default-features = false }
p256 = "0.13.2"
paste = "1.0.15"
prometheus = "0.14.0"
quartz_nbt = "0.2.9"
quick-xml = "0.37.5"
rand = "=0.8.5" # Locked on 0.8 until argon2 and p256 update to 0.9
rand_chacha = "=0.3.1" # Locked on 0.3 until we can update rand to 0.9
redis = "=0.29.5" # Locked on 0.29 until deadpool-redis updates to 0.30
regex = "1.11.1"
reqwest = { version = "0.12.15", default-features = false }
rust-s3 = { version = "0.35.1", default-features = false, features = [
"fail-on-err",
"tags",
"tokio-rustls-tls",
] }
rust_decimal = { version = "1.37.1", features = [
"serde-with-float",
"serde-with-str",
] }
rust_iso3166 = "0.1.14"
rusty-money = "0.4.1"
sentry = { version = "0.38.1", default-features = false, features = [
"backtrace",
"contexts",
"debug-images",
"panic",
"reqwest",
"rustls",
] }
sentry-actix = "0.38.1"
serde = "1.0.219"
serde-xml-rs = "0.8.0" # Also an XML (de)serializer, consider dropping yaserde in favor of this
serde_bytes = "0.11.17"
serde_cbor = "0.11.2"
serde_ini = "0.2.0"
serde_json = "1.0.140"
serde_with = "3.12.0"
sha1 = "0.10.6"
sha1_smol = { version = "1.0.1", features = ["std"] }
sha2 = "0.10.9"
spdx = "0.10.8"
sqlx = { version = "0.8.5", default-features = false }
sysinfo = { version = "0.35.1", default-features = false }
tar = "0.4.44"
tauri = "2.5.1"
tauri-build = "2.2.0"
tauri-plugin-deep-link = "2.2.1"
tauri-plugin-dialog = "2.2.1"
tauri-plugin-opener = "2.2.6"
tauri-plugin-os = "2.2.1"
tauri-plugin-single-instance = "2.2.3"
tauri-plugin-updater = { version = "2.7.1", default-features = false, features = [
"rustls-tls",
] }
tauri-plugin-window-state = "2.2.2"
tempfile = "3.20.0"
theseus = { path = "packages/app-lib" }
thiserror = "2.0.12"
tikv-jemalloc-ctl = "0.6.0"
tikv-jemallocator = "0.6.0"
tokio = "1.45.0"
tokio-stream = "0.1.17"
tokio-util = "0.7.15"
totp-rs = "5.7.0"
tracing = "0.1.41"
tracing-actix-web = "0.7.18"
tracing-error = "0.2.1"
tracing-subscriber = "0.3.19"
url = "2.5.4"
urlencoding = "2.1.3"
uuid = "1.16.0"
validator = "0.20.0"
webp = { version = "0.3.0", default-features = false }
whoami = "1.6.0"
winreg = "0.55.0"
woothee = "0.13.0"
yaserde = "0.12.0"
zip = { version = "3.0.0", default-features = false, features = [
"bzip2",
"deflate",
"deflate64",
"zstd",
] }
zxcvbn = "3.1.0"
[patch.crates-io]
wry = { git = "https://github.com/modrinth/wry", rev = "cafdaa9" }
# Optimize for speed and reduce size on release builds
[profile.release]
opt-level = "s" # Optimize for binary size
strip = true # Remove debug symbols
lto = true # Enables link to optimizations
panic = "abort" # Strip expensive panic clean-up logic
codegen-units = 1 # Compile crates one after another so the compiler can optimize better
[profile.dev.package.sqlx-macros]
opt-level = 3

View File

@@ -6,9 +6,6 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
theseus = { path = "../../packages/app-lib", features = ["cli"] }
tokio = { version = "1", features = ["full"] }
webbrowser = "1.0.4"
enumset = "1.1"
tracing = "0.1.37"
theseus = { workspace = true, features = ["cli"] }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
enumset.workspace = true

View File

@@ -15,8 +15,7 @@ pub async fn authenticate_run() -> theseus::Result<Credentials> {
println!("A browser window will now open, follow the login flow there.");
let login = minecraft_auth::begin_login().await?;
println!("URL {}", login.redirect_uri.as_str());
webbrowser::open(login.redirect_uri.as_str())?;
println!("Open URL {} in a browser", login.redirect_uri.as_str());
println!("Please enter URL code: ");
let mut input = String::new();

View File

@@ -8,47 +8,45 @@ edition = "2024"
build = "build.rs"
[build-dependencies]
tauri-build = { version = "2.2.0", features = ["codegen"] }
tauri-build = { workspace = true, features = ["codegen"] }
[dependencies]
theseus = { path = "../../packages/app-lib", features = ["tauri"] }
theseus = { workspace = true, features = ["tauri"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_with = "3.0.0"
serde_json.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_with.workspace = true
tauri = { version = "2.5.1", features = ["devtools", "macos-private-api", "protocol-asset", "unstable"] }
tauri-plugin-window-state = "2.2.0"
tauri-plugin-deep-link = "2.2.0"
tauri-plugin-os = "2.2.0"
tauri-plugin-opener = "2.2.6"
tauri-plugin-dialog = "2.2.0"
tauri-plugin-updater = { version = "2.3.0" }
tauri-plugin-single-instance = { version = "2.2.0" }
tauri = { workspace = true, features = ["devtools", "macos-private-api", "protocol-asset", "unstable"] }
tauri-plugin-window-state.workspace = true
tauri-plugin-deep-link.workspace = true
tauri-plugin-os.workspace = true
tauri-plugin-opener.workspace = true
tauri-plugin-dialog.workspace = true
tauri-plugin-updater.workspace = true
tauri-plugin-single-instance.workspace = true
tokio = { version = "1", features = ["full"] }
thiserror = "2.0.12"
daedalus = { path = "../../packages/daedalus" }
chrono = "0.4.26"
either = "1.15"
tokio = { workspace = true, features = ["time"] }
thiserror.workspace = true
daedalus.workspace = true
chrono.workspace = true
either.workspace = true
url = "2.2"
urlencoding = "2.1"
uuid = { version = "1.1", features = ["serde", "v4"] }
url.workspace = true
urlencoding.workspace = true
uuid = { workspace = true, features = ["serde", "v4"] }
tracing = "0.1.37"
tracing-error = "0.2.0"
tracing.workspace = true
tracing-error.workspace = true
dashmap = "6.1.0"
paste = "1.0.15"
enumset = { version = "1.1", features = ["serde"] }
dashmap.workspace = true
paste.workspace = true
enumset = { workspace = true, features = ["serde"] }
opener = { version = "0.7.2", features = ["reveal", "dbus-vendored"] }
native-dialog = "0.9.0"
native-dialog.workspace = true
[target.'cfg(target_os = "linux")'.dependencies]
tauri-plugin-updater = { version = "2.3.0", optional = true, features = ["native-tls-vendored", "zip"], default-features = false }
tauri-plugin-updater = { workspace = true, optional = true }
[features]
# by default Tauri runs in production mode

View File

@@ -1,4 +1,6 @@
use serde::{Deserialize, Serialize};
use tauri::Runtime;
use tauri_plugin_opener::OpenerExt;
use theseus::{
handler,
prelude::{CommandPayload, DirectoryInfo},
@@ -74,29 +76,29 @@ pub async fn should_disable_mouseover() -> bool {
}
#[tauri::command]
pub fn highlight_in_folder(path: PathBuf) {
let res = opener::reveal(path);
if let Err(e) = res {
pub fn highlight_in_folder<R: Runtime>(
app: tauri::AppHandle<R>,
path: PathBuf,
) {
if let Err(e) = app.opener().reveal_item_in_dir(path) {
tracing::error!("Failed to highlight file in folder: {}", e);
}
}
#[tauri::command]
pub fn open_path(path: PathBuf) {
let res = opener::open(path);
if let Err(e) = res {
pub fn open_path<R: Runtime>(app: tauri::AppHandle<R>, path: PathBuf) {
if let Err(e) = app.opener().open_path(path.to_string_lossy(), None::<&str>)
{
tracing::error!("Failed to open path: {}", e);
}
}
#[tauri::command]
pub fn show_launcher_logs_folder() {
pub fn show_launcher_logs_folder<R: Runtime>(app: tauri::AppHandle<R>) {
let path = DirectoryInfo::launcher_logs_dir().unwrap_or_default();
// failure to get folder just opens filesystem
// (ie: if in debug mode only and launcher_logs never created)
open_path(path);
open_path(app, path);
}
// Get opening command

View File

@@ -7,25 +7,24 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
daedalus = { path = "../../packages/daedalus" }
tokio = { version = "1", features = ["full"] }
futures = "0.3.25"
dotenvy = "0.15.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde-xml-rs = "0.6.0"
lazy_static = "1.4.0"
thiserror = "2.0"
reqwest = { version = "0.12.15", default-features = false, features = ["stream", "json", "rustls-tls-native-roots"] }
async_zip = { version = "0.0.17", features = ["full"] }
chrono = { version = "0.4", features = ["serde"] }
bytes = "1.6.0"
rust-s3 = { version = "0.35.1", default-features = false, features = ["fail-on-err", "tags", "tokio-rustls-tls"] }
dashmap = "6.1.0"
sha1_smol = { version = "1.0.0", features = ["std"] }
indexmap = { version = "2.2.6", features = ["serde"] }
itertools = "0.14.0"
tracing-error = "0.2.0"
daedalus.workspace = true
tokio = { workspace = true, features = ["sync", "macros", "rt-multi-thread"] }
futures.workspace = true
dotenvy.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde-xml-rs.workspace = true
thiserror.workspace = true
reqwest = { workspace = true, features = ["stream", "json", "rustls-tls-native-roots"] }
async_zip = { workspace = true, features = ["chrono", "tokio-fs", "deflate", "bzip2", "zstd", "deflate64"] }
chrono = { workspace = true, features = ["serde"] }
bytes.workspace = true
rust-s3.workspace = true
dashmap.workspace = true
sha1_smol.workspace = true
indexmap = { workspace = true, features = ["serde"] }
itertools.workspace = true
tracing-error.workspace = true
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter"] }

View File

@@ -3,59 +3,57 @@ use bytes::Bytes;
use s3::creds::Credentials;
use s3::{Bucket, Region};
use serde::de::DeserializeOwned;
use std::sync::Arc;
use std::sync::{Arc, LazyLock};
use tokio::sync::Semaphore;
lazy_static::lazy_static! {
static ref BUCKET: Bucket = {
let region = dotenvy::var("S3_REGION").unwrap();
let b = Bucket::new(
&dotenvy::var("S3_BUCKET_NAME").unwrap(),
if &*region == "r2" {
Region::R2 {
account_id: dotenvy::var("S3_URL").unwrap(),
}
} else {
Region::Custom {
region: region.clone(),
endpoint: dotenvy::var("S3_URL").unwrap(),
}
},
Credentials::new(
Some(&*dotenvy::var("S3_ACCESS_TOKEN").unwrap()),
Some(&*dotenvy::var("S3_SECRET").unwrap()),
None,
None,
None,
).unwrap(),
).unwrap();
if region == "path-style" {
*b.with_path_style()
static BUCKET: LazyLock<Bucket> = LazyLock::new(|| {
let region = dotenvy::var("S3_REGION").unwrap();
let b = Bucket::new(
&dotenvy::var("S3_BUCKET_NAME").unwrap(),
if &*region == "r2" {
Region::R2 {
account_id: dotenvy::var("S3_URL").unwrap(),
}
} else {
*b
}
};
}
Region::Custom {
region: region.clone(),
endpoint: dotenvy::var("S3_URL").unwrap(),
}
},
Credentials::new(
Some(&*dotenvy::var("S3_ACCESS_TOKEN").unwrap()),
Some(&*dotenvy::var("S3_SECRET").unwrap()),
None,
None,
None,
)
.unwrap(),
)
.unwrap();
lazy_static::lazy_static! {
pub static ref REQWEST_CLIENT: reqwest::Client = {
let mut headers = reqwest::header::HeaderMap::new();
if let Ok(header) = reqwest::header::HeaderValue::from_str(&format!(
"modrinth/daedalus/{} (support@modrinth.com)",
env!("CARGO_PKG_VERSION")
)) {
headers.insert(reqwest::header::USER_AGENT, header);
}
if region == "path-style" {
*b.with_path_style()
} else {
*b
}
});
reqwest::Client::builder()
.tcp_keepalive(Some(std::time::Duration::from_secs(10)))
.timeout(std::time::Duration::from_secs(15))
.default_headers(headers)
.build()
.unwrap()
};
}
pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
let mut headers = reqwest::header::HeaderMap::new();
if let Ok(header) = reqwest::header::HeaderValue::from_str(&format!(
"modrinth/daedalus/{} (support@modrinth.com)",
env!("CARGO_PKG_VERSION")
)) {
headers.insert(reqwest::header::USER_AGENT, header);
}
reqwest::Client::builder()
.tcp_keepalive(Some(std::time::Duration::from_secs(10)))
.timeout(std::time::Duration::from_secs(15))
.default_headers(headers)
.build()
.unwrap()
});
#[tracing::instrument(skip(bytes, semaphore))]
pub async fn upload_file_to_bucket(

View File

@@ -11,74 +11,72 @@ name = "labrinth"
path = "src/main.rs"
[dependencies]
actix-web = "4.10.2"
actix-rt = "2.9.0"
actix-multipart = "0.7.2"
actix-cors = "0.7.1"
actix-ws = "0.3.0"
actix-files = "0.6.5"
prometheus = "0.13.4" # Locked on 0.13 until actix updates to 0.14
actix-web-prom = { version = "0.9.0", features = ["process"] }
actix-web.workspace = true
actix-rt.workspace = true
actix-multipart.workspace = true
actix-cors.workspace = true
actix-ws.workspace = true
actix-files.workspace = true
prometheus.workspace = true
actix-web-prom = { workspace = true, features = ["process"] }
tracing = "0.1.41"
tracing-actix-web = "0.7.18"
console-subscriber = "0.4.1"
tracing.workspace = true
tracing-actix-web.workspace = true
console-subscriber.workspace = true
tokio = { version = "1.35.1", features = ["sync", "rt-multi-thread"] }
tokio-stream = "0.1.14"
tokio = { workspace = true, features = ["sync", "rt-multi-thread"] }
tokio-stream.workspace = true
futures = "0.3.30"
futures-util = "0.3.30"
async-trait = "0.1.70"
dashmap = "6.1.0"
lazy_static = "1.4.0"
futures.workspace = true
futures-util.workspace = true
async-trait.workspace = true
dashmap.workspace = true
meilisearch-sdk = "0.28.0"
rust-s3 = { version = "0.35.1", default-features = false, features = ["fail-on-err", "tags", "tokio-rustls-tls"] }
reqwest = { version = "0.12.15", features = ["json", "multipart"] }
hyper = { version = "1.6", features = ["full"] }
hyper-tls = "0.6.0"
hyper-util = "0.1.11"
meilisearch-sdk = { workspace = true, features = ["reqwest"] }
rust-s3.workspace = true
reqwest = { workspace = true, features = ["http2", "rustls-tls-webpki-roots", "json", "multipart"] }
hyper-tls.workspace = true
hyper-util.workspace = true
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_with = "3.0.0"
chrono = { version = "0.4.26", features = ["serde"] }
yaserde = "0.12.0"
yaserde_derive = "0.12.0"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_with.workspace = true
chrono = { workspace = true, features = ["serde"] }
yaserde = { workspace = true, features = ["derive"] }
rand = "0.8.5" # Locked on 0.8 until argon2 updates to 0.9
rand_chacha = "0.3.1" # Locked on 0.3 until we can update rand to 0.9
bytes = "1.4.0"
base64 = "0.22.1"
sha1 = { version = "0.10.6", features = ["std"] }
sha2 = "0.10.9"
hmac = "0.12.1"
argon2 = { version = "0.5.0", features = ["std"] }
murmur2 = "0.1.0"
bitflags = "2.4.0"
hex = "0.4.3"
zxcvbn = "3.1.0"
totp-rs = { version = "5.0.2", features = ["gen_secret"] }
rand.workspace = true
rand_chacha.workspace = true
bytes.workspace = true
base64.workspace = true
sha1.workspace = true
sha2.workspace = true
hmac.workspace = true
argon2.workspace = true
murmur2.workspace = true
bitflags.workspace = true
hex.workspace = true
zxcvbn.workspace = true
totp-rs = { workspace = true, features = ["gen_secret"] }
url = "2.4.0"
urlencoding = "2.1.2"
url.workspace = true
urlencoding.workspace = true
zip = "2.6.1"
zip.workspace = true
itertools = "0.14.0"
itertools.workspace = true
validator = { version = "0.20.0", features = ["derive"] }
regex = "1.10.2"
censor = "0.3.0"
spdx = { version = "0.10.3", features = ["text"] }
validator = { workspace = true, features = ["derive"] }
regex.workspace = true
censor.workspace = true
spdx = { workspace = true, features = ["text"] }
dotenvy = "0.15.7"
thiserror = "2.0.12"
either = "1.13"
dotenvy.workspace = true
thiserror.workspace = true
either.workspace = true
sqlx = { version = "0.8.2", features = [
"runtime-tokio-rustls",
sqlx = { workspace = true, features = [
"runtime-tokio",
"tls-rustls-ring",
"postgres",
"chrono",
"macros",
@@ -86,51 +84,49 @@ sqlx = { version = "0.8.2", features = [
"rust_decimal",
"json",
] }
rust_decimal = { version = "1.33.1", features = [
rust_decimal = { workspace = true, features = [
"serde-with-float",
"serde-with-str",
] }
redis = { version = "0.29.5", features = ["tokio-comp", "ahash", "r2d2"] } # Locked on 0.29 until deadpool-redis updates to 0.30
deadpool-redis = "0.20.0"
clickhouse = { version = "0.13.2", features = ["uuid", "time"] }
uuid = { version = "1.2.2", features = ["v4", "fast-rng", "serde"] }
redis = { workspace = true, features = ["tokio-comp", "ahash", "r2d2"] } # Locked on 0.29 until deadpool-redis updates to 0.30
deadpool-redis.workspace = true
clickhouse = { workspace = true, features = ["uuid", "time"] }
uuid = { workspace = true, features = ["v4", "fast-rng", "serde"] }
maxminddb = "0.26.0"
flate2 = "1.0.25"
tar = "0.4.38"
maxminddb.workspace = true
flate2.workspace = true
tar.workspace = true
sentry = { version = "0.37.0", default-features = false, features = ["backtrace", "contexts", "debug-images", "panic", "rustls", "reqwest"] }
sentry-actix = "0.37.0"
sentry.workspace = true
sentry-actix.workspace = true
image = "0.25.6"
color-thief = "0.2.2"
webp = "0.3.0"
image = { workspace = true, features = ["avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp"] }
color-thief.workspace = true
webp.workspace = true
woothee = "0.13.0"
woothee.workspace = true
lettre = "0.11.3"
lettre.workspace = true
derive-new = "0.7.0"
rust_iso3166 = "0.1.11"
rust_iso3166.workspace = true
async-stripe = { version = "0.41.0", features = ["runtime-tokio-hyper-rustls"] }
rusty-money = "0.4.1"
json-patch = "4.0.0"
async-stripe = { workspace = true, features = ["billing", "checkout", "connect", "webhook-events"] }
rusty-money.workspace = true
json-patch.workspace = true
ariadne = { path = "../../packages/ariadne" }
ariadne.workspace = true
clap = { version = "4.5", features = ["derive"] }
iana-time-zone = "0.1.61"
clap = { workspace = true, features = ["derive"] }
[target.'cfg(target_os = "linux")'.dependencies]
tikv-jemallocator = { version = "0.6.0", features = ["profiling", "unprefixed_malloc_on_supported_platforms"] }
tikv-jemalloc-ctl = { version = "0.6.0", features = ["stats"] }
jemalloc_pprof = { version = "0.7.0", features = ["flamegraph"] }
tikv-jemallocator = { workspace = true, features = ["profiling", "unprefixed_malloc_on_supported_platforms"] }
tikv-jemalloc-ctl = { workspace = true, features = ["stats"] }
jemalloc_pprof = { workspace = true, features = ["flamegraph"] }
[dev-dependencies]
actix-http = "3.4.0"
actix-http.workspace = true
[build-dependencies]
dotenv-build = "0.1.1"
chrono = "0.4.38"
iana-time-zone = "0.1.60"
dotenv-build.workspace = true
chrono.workspace = true
iana-time-zone.workspace = true

View File

@@ -111,10 +111,10 @@ pub async fn init_oauth(
.map_err(|e| {
OAuthError::redirect(e, &oauth_info.state, &redirect_uri)
})?;
let redirect_uris = OAuthRedirectUris::new(
oauth_info.redirect_uri.clone(),
redirect_uri.clone(),
);
let redirect_uris = OAuthRedirectUris {
original: oauth_info.redirect_uri.clone(),
validated: redirect_uri.clone(),
};
match existing_authorization {
Some(existing_authorization)
if existing_authorization.scopes.contains(requested_scopes) =>

View File

@@ -3,7 +3,7 @@ use crate::auth::oauth::OAuthErrorType;
use crate::database::models::OAuthClientId;
use serde::{Deserialize, Serialize};
#[derive(derive_new::new, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct OAuthRedirectUris {
pub original: Option<String>,
pub validated: ValidatedRedirectUri,

View File

@@ -132,6 +132,8 @@ pub async fn payouts(
}
mod version_updater {
use std::sync::LazyLock;
use crate::database::models::legacy_loader_fields::MinecraftGameVersion;
use crate::database::redis::RedisPool;
use chrono::{DateTime, Utc};
@@ -197,36 +199,45 @@ mod version_updater {
("3D Shareware v1.34", "3D-Shareware-v1.34"),
];
lazy_static::lazy_static! {
/// Mojank for some reason has versions released at the same DateTime. This hardcodes them to fix this,
/// as most of our ordering logic is with DateTime
static ref HALL_OF_SHAME_2: [(&'static str, DateTime<Utc>); 4] = [
(
"1.4.5",
chrono::DateTime::parse_from_rfc3339("2012-12-19T22:00:00+00:00")
/// Mojank for some reason has versions released at the same DateTime. This hardcodes them to fix this,
/// as most of our ordering logic is with DateTime
static HALL_OF_SHAME_2: LazyLock<[(&'static str, DateTime<Utc>); 4]> =
LazyLock::new(|| {
[
(
"1.4.5",
chrono::DateTime::parse_from_rfc3339(
"2012-12-19T22:00:00+00:00",
)
.unwrap()
.into(),
),
(
"1.4.6",
chrono::DateTime::parse_from_rfc3339("2012-12-19T22:00:01+00:00")
),
(
"1.4.6",
chrono::DateTime::parse_from_rfc3339(
"2012-12-19T22:00:01+00:00",
)
.unwrap()
.into(),
),
(
"1.6.3",
chrono::DateTime::parse_from_rfc3339("2013-09-13T10:54:41+00:00")
),
(
"1.6.3",
chrono::DateTime::parse_from_rfc3339(
"2013-09-13T10:54:41+00:00",
)
.unwrap()
.into(),
),
(
"13w37b",
chrono::DateTime::parse_from_rfc3339("2013-09-13T10:54:42+00:00")
),
(
"13w37b",
chrono::DateTime::parse_from_rfc3339(
"2013-09-13T10:54:42+00:00",
)
.unwrap()
.into(),
),
];
}
),
]
});
for version in input.versions.into_iter() {
let mut name = version.id;

View File

@@ -117,11 +117,10 @@ impl GalleryItem {
}
}
#[derive(derive_new::new)]
pub struct ModCategory {
project_id: ProjectId,
category_id: CategoryId,
is_additional: bool,
pub project_id: ProjectId,
pub category_id: CategoryId,
pub is_additional: bool,
}
impl ModCategory {
@@ -245,12 +244,18 @@ impl ProjectBuilder {
let project_id = self.project_id;
let mod_categories = categories
.into_iter()
.map(|c| ModCategory::new(project_id, c, false))
.chain(
additional_categories
.into_iter()
.map(|c| ModCategory::new(project_id, c, true)),
)
.map(|category_id| ModCategory {
project_id,
category_id,
is_additional: false,
})
.chain(additional_categories.into_iter().map(|category_id| {
ModCategory {
project_id,
category_id,
is_additional: true,
}
}))
.collect_vec();
ModCategory::insert_many(mod_categories, &mut *transaction).await?;

View File

@@ -229,7 +229,10 @@ impl VersionBuilder {
let loader_versions = loaders
.iter()
.map(|l| LoaderVersion::new(*l, version_id))
.map(|&loader_id| LoaderVersion {
loader_id,
version_id,
})
.collect_vec();
LoaderVersion::insert_many(loader_versions, transaction).await?;
@@ -239,7 +242,7 @@ impl VersionBuilder {
}
}
#[derive(derive_new::new, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct LoaderVersion {
pub loader_id: LoaderId,
pub version_id: VersionId,

View File

@@ -152,7 +152,7 @@ async fn main() -> std::io::Result<()> {
.expect("Failed to register redis metrics");
#[cfg(target_os = "linux")]
labrinth::routes::debug::jemalloc_mmeory_stats(&prometheus.registry)
labrinth::routes::debug::jemalloc_memory_stats(&prometheus.registry)
.expect("Failed to register jemalloc metrics");
let labrinth_config = labrinth::app_setup(

View File

@@ -50,7 +50,7 @@ fn require_profiling_activated(
}
}
pub fn jemalloc_mmeory_stats(
pub fn jemalloc_memory_stats(
registry: &Registry,
) -> Result<(), prometheus::Error> {
let allocated_mem = IntGauge::new(

View File

@@ -13,7 +13,7 @@ use crate::util::captcha::check_hcaptcha;
use crate::util::env::parse_strings_from_var;
use crate::util::ext::get_image_ext;
use crate::util::img::upload_image_optimized;
use crate::util::validate::{RE_URL_SAFE, validation_errors_to_string};
use crate::util::validate::validation_errors_to_string;
use actix_web::web::{Data, Query, ServiceConfig, scope};
use actix_web::{HttpRequest, HttpResponse, delete, get, patch, post, web};
use argon2::password_hash::SaltString;
@@ -1318,7 +1318,7 @@ pub async fn sign_up_sendy(email: &str) -> Result<(), AuthenticationError> {
#[derive(Deserialize, Validate)]
pub struct NewAccount {
#[validate(length(min = 1, max = 39), regex(path = *RE_URL_SAFE))]
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_URL_SAFE))]
pub username: String,
#[validate(length(min = 8, max = 256))]
pub password: String,

View File

@@ -12,7 +12,7 @@ use crate::{auth::get_user_from_headers, database};
use actix_web::{HttpRequest, HttpResponse, get, route, web};
use sqlx::PgPool;
use std::collections::HashSet;
use yaserde_derive::YaSerialize;
use yaserde::YaSerialize;
pub fn config(cfg: &mut web::ServiceConfig) {
cfg.service(maven_metadata);

View File

@@ -9,8 +9,6 @@ use crate::models::v2::user::LegacyUser;
use crate::queue::session::AuthQueue;
use crate::routes::{ApiError, v2_reroute, v3};
use actix_web::{HttpRequest, HttpResponse, delete, get, patch, web};
use lazy_static::lazy_static;
use regex::Regex;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use std::sync::Arc;
@@ -135,20 +133,16 @@ pub async fn projects_list(
}
}
lazy_static! {
static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap();
}
#[derive(Serialize, Deserialize, Validate)]
pub struct EditUser {
#[validate(length(min = 1, max = 39), regex(path = *RE_URL_SAFE))]
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_URL_SAFE))]
pub username: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option"
)]
#[validate(length(min = 1, max = 64), regex(path = *RE_URL_SAFE))]
#[validate(length(min = 1, max = 64), regex(path = *crate::util::validate::RE_URL_SAFE))]
pub name: Option<Option<String>>,
#[serde(
default,

View File

@@ -906,11 +906,11 @@ pub async fn edit_project_categories(
categories: &Vec<String>,
perms: &ProjectPermissions,
project_id: db_ids::ProjectId,
additional: bool,
is_additional: bool,
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<(), ApiError> {
if !perms.contains(ProjectPermissions::EDIT_DETAILS) {
let additional_str = if additional { "additional " } else { "" };
let additional_str = if is_additional { "additional " } else { "" };
return Err(ApiError::CustomAuthentication(format!(
"You do not have the permissions to edit the {additional_str}categories of this project!"
)));
@@ -928,7 +928,11 @@ pub async fn edit_project_categories(
let mcategories = category_ids
.values()
.map(|x| ModCategory::new(project_id, *x, additional))
.map(|&category_id| ModCategory {
project_id,
category_id,
is_additional,
})
.collect::<Vec<_>>();
mod_categories.extend(mcategories);
}
@@ -1081,7 +1085,6 @@ pub async fn dependency_list(
}
}
#[derive(derive_new::new)]
pub struct CategoryChanges<'a> {
pub categories: &'a Option<Vec<String>>,
pub add_categories: &'a Option<Vec<String>>,
@@ -1241,11 +1244,11 @@ pub async fn projects_edit(
&categories,
&project.categories,
project.inner.id as db_ids::ProjectId,
CategoryChanges::new(
&bulk_edit_project.categories,
&bulk_edit_project.add_categories,
&bulk_edit_project.remove_categories,
),
CategoryChanges {
categories: &bulk_edit_project.categories,
add_categories: &bulk_edit_project.add_categories,
remove_categories: &bulk_edit_project.remove_categories,
},
3,
false,
&mut transaction,
@@ -1256,11 +1259,12 @@ pub async fn projects_edit(
&categories,
&project.additional_categories,
project.inner.id as db_ids::ProjectId,
CategoryChanges::new(
&bulk_edit_project.additional_categories,
&bulk_edit_project.add_additional_categories,
&bulk_edit_project.remove_additional_categories,
),
CategoryChanges {
categories: &bulk_edit_project.additional_categories,
add_categories: &bulk_edit_project.add_additional_categories,
remove_categories: &bulk_edit_project
.remove_additional_categories,
},
256,
true,
&mut transaction,
@@ -1383,11 +1387,11 @@ pub async fn bulk_edit_project_categories(
))
})?
.id;
mod_categories.push(ModCategory::new(
mod_categories.push(ModCategory {
project_id,
category_id,
is_additional,
));
});
}
ModCategory::insert_many(mod_categories, &mut *transaction).await?;
}

View File

@@ -1,8 +1,6 @@
use std::{collections::HashMap, sync::Arc};
use actix_web::{HttpRequest, HttpResponse, web};
use lazy_static::lazy_static;
use regex::Regex;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use validator::Validate;
@@ -358,13 +356,9 @@ pub async fn orgs_list(
}
}
lazy_static! {
static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap();
}
#[derive(Serialize, Deserialize, Validate)]
pub struct EditUser {
#[validate(length(min = 1, max = 39), regex(path = *RE_URL_SAFE))]
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_URL_SAFE))]
pub username: Option<String>,
#[serde(
default,

View File

@@ -287,10 +287,10 @@ pub async fn version_edit_helper(
ApiError::Validation(validation_errors_to_string(err, None))
})?;
let version_id = info.0;
let id = version_id.into();
let version_id = info.0.into();
let result = database::models::Version::get(id, &**pool, &redis).await?;
let result =
database::models::Version::get(version_id, &**pool, &redis).await?;
if let Some(version_item) = result {
let team_member =
@@ -345,7 +345,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
name.trim(),
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -359,7 +359,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
number,
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -373,7 +373,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
version_type.as_str(),
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -384,7 +384,7 @@ pub async fn version_edit_helper(
"
DELETE FROM dependencies WHERE dependent_id = $1
",
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -448,7 +448,7 @@ pub async fn version_edit_helper(
WHERE version_id = $1
AND field_id = ANY($2)
",
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
&loader_field_ids
)
.execute(&mut *transaction)
@@ -476,7 +476,7 @@ pub async fn version_edit_helper(
.remove(&loader_field.id)
.unwrap_or_default();
let vf: VersionField = VersionField::check_parse(
version_id.into(),
version_id,
loader_field.clone(),
vf_value.clone(),
enum_variants,
@@ -493,7 +493,7 @@ pub async fn version_edit_helper(
"
DELETE FROM loaders_versions WHERE version_id = $1
",
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -513,7 +513,10 @@ pub async fn version_edit_helper(
.to_string(),
)
})?;
loader_versions.push(LoaderVersion::new(loader_id, id));
loader_versions.push(LoaderVersion {
loader_id,
version_id,
});
}
LoaderVersion::insert_many(loader_versions, &mut transaction)
.await?;
@@ -535,7 +538,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
featured,
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -549,7 +552,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
body,
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -569,7 +572,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
*downloads as i32,
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -604,7 +607,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
status.as_str(),
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;
@@ -652,7 +655,7 @@ pub async fn version_edit_helper(
WHERE (id = $2)
",
ordering.to_owned() as Option<i32>,
id as database::models::ids::VersionId,
version_id as database::models::ids::VersionId,
)
.execute(&mut *transaction)
.await?;

View File

@@ -1,14 +1,13 @@
use std::sync::LazyLock;
use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
use validator::{ValidationErrors, ValidationErrorsKind};
use crate::models::pats::Scopes;
lazy_static! {
pub static ref RE_URL_SAFE: Regex =
Regex::new(r#"^[a-zA-Z0-9!@$()`.+,_"-]*$"#).unwrap();
}
pub static RE_URL_SAFE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap());
//TODO: In order to ensure readability, only the first error is printed, this may need to be expanded on in the future!
pub fn validation_errors_to_string(

View File

@@ -5,75 +5,72 @@ authors = ["Jai A <jaiagr+gpg@pm.me>"]
edition = "2024"
[dependencies]
bytes = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_ini = "0.2.0"
sha1_smol = { version = "1.0.0", features = ["std"] }
sha2 = "0.10.9"
url = { version = "2.2", features = ["serde"] }
uuid = { version = "1.1", features = ["serde", "v4"] }
zip = "2.6.1"
async_zip = { version = "0.0.17", features = ["chrono", "tokio-fs", "deflate", "bzip2", "zstd", "deflate64"] }
flate2 = "1.1.1"
tempfile = "3.5.0"
dashmap = { version = "6.1.0", features = ["serde"] }
quick-xml = { version = "0.37", features = ["async-tokio"] }
enumset = "1.1"
bytes.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_ini.workspace = true
sha1_smol.workspace = true
sha2.workspace = true
url = { workspace = true, features = ["serde"] }
uuid = { workspace = true, features = ["serde", "v4"] }
zip.workspace = true
async_zip = { workspace = true, features = ["chrono", "tokio-fs", "deflate", "bzip2", "zstd", "deflate64"] }
flate2.workspace = true
tempfile.workspace = true
dashmap = { workspace = true, features = ["serde"] }
quick-xml = { workspace = true, features = ["async-tokio"] }
enumset.workspace = true
chrono = { version = "0.4.19", features = ["serde"] }
daedalus = { path = "../../packages/daedalus" }
dirs = "6.0.0"
chrono = { workspace = true, features = ["serde"] }
daedalus.workspace = true
dirs.workspace = true
regex = "1.5"
sys-info = "0.9.0"
sysinfo = "0.35.0"
thiserror = "2.0.12"
either = "1.13"
regex.workspace = true
sysinfo = { workspace = true, features = ["system", "disk"] }
thiserror.workspace = true
either.workspace = true
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.18", features = ["chrono", "env-filter"] }
tracing-error = "0.2.0"
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["chrono", "env-filter"] }
tracing-error.workspace = true
paste = { version = "1.0" }
paste.workspace = true
tauri = { version = "2.5.1", optional = true }
indicatif = { version = "0.17.3", optional = true }
tauri = { workspace = true, optional = true }
indicatif = { workspace = true, optional = true }
async-tungstenite = { version = "0.29.1", features = ["tokio-runtime", "tokio-rustls-webpki-roots"] }
futures = "0.3"
reqwest = { version = "0.12.15", features = ["json", "stream", "deflate", "gzip", "brotli", "rustls-tls", "charset", "http2", "macos-system-configuration"], default-features = false }
tokio = { version = "1", features = ["full"] }
tokio-util = "0.7"
async-recursion = "1.0.4"
fs4 = { version = "0.13", features = ["tokio"] }
async-walkdir = "2.1"
async-compression = { version = "0.4", default-features = false, features = ["tokio", "gzip"] }
async-tungstenite = { workspace = true, features = ["tokio-runtime", "tokio-rustls-webpki-roots"] }
futures = { workspace = true, features = ["async-await", "alloc"] }
reqwest = { workspace = true, features = ["json", "stream", "deflate", "gzip", "brotli", "rustls-tls-webpki-roots", "charset", "http2", "macos-system-configuration"] }
tokio = { workspace = true, features = ["time", "io-util", "net", "sync", "fs", "macros", "process"] }
tokio-util = { workspace = true, features = ["compat"] }
async-recursion.workspace = true
fs4 = { workspace = true, features = ["tokio"] }
async-walkdir.workspace = true
async-compression = { workspace = true, features = ["tokio", "gzip"] }
notify = { version = "8.0.0", default-features = false }
notify-debouncer-mini = { version = "0.6.0", default-features = false }
notify.workspace = true
notify-debouncer-mini.workspace = true
lazy_static = "1.4.0"
dunce = "1.0.3"
dunce.workspace = true
whoami = "1.4.0"
whoami.workspace = true
discord-rich-presence = "0.2.4"
discord-rich-presence.workspace = true
p256 = { version = "0.13.2", features = ["ecdsa"] }
rand = "0.8"
byteorder = "1.5.0"
base64 = "0.22.1"
p256 = { workspace = true, features = ["ecdsa"] }
rand.workspace = true
base64.workspace = true
sqlx = { version = "0.8.2", features = [ "runtime-tokio", "sqlite", "macros" ] }
sqlx = { workspace = true, features = ["runtime-tokio", "sqlite", "macros", "migrate", "json"] }
quartz_nbt = { version = "0.2", features = ["serde"] }
hickory-resolver = "0.25"
quartz_nbt = { workspace = true, features = ["serde"] }
hickory-resolver.workspace = true
ariadne = { path = "../ariadne" }
ariadne.workspace = true
[target.'cfg(windows)'.dependencies]
winreg = "0.55.0"
winreg.workspace = true
[features]
tauri = ["dep:tauri"]

View File

@@ -6,6 +6,7 @@ use dashmap::DashMap;
use reqwest::Method;
use serde::Deserialize;
use std::path::PathBuf;
use sysinfo::{MemoryRefreshKind, RefreshKind};
use crate::util::io;
use crate::util::jre::extract_java_majorminor_version;
@@ -175,11 +176,10 @@ pub async fn test_jre(
// Gets maximum memory in KiB.
pub async fn get_max_memory() -> crate::Result<u64> {
Ok(sys_info::mem_info()
.map_err(|_| {
crate::Error::from(crate::ErrorKind::LauncherError(
"Unable to get computer memory".to_string(),
))
})?
.total)
Ok(sysinfo::System::new_with_specifics(
RefreshKind::nothing()
.with_memory(MemoryRefreshKind::nothing().with_ram()),
)
.total_memory()
/ 1024)
}

View File

@@ -17,7 +17,6 @@ use either::Either;
use enumset::{EnumSet, EnumSetType};
use fs4::tokio::AsyncFileExt;
use futures::StreamExt;
use lazy_static::lazy_static;
use quartz_nbt::{NbtCompound, NbtTag};
use regex::{Regex, RegexBuilder};
use serde::{Deserialize, Serialize};
@@ -25,6 +24,7 @@ use std::cmp::Reverse;
use std::io::Cursor;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use tokio::io::AsyncWriteExt;
use tokio_util::compat::FuturesAsyncWriteCompatExt;
use url::Url;
@@ -548,17 +548,19 @@ pub async fn backup_world(instance: &Path, world: &str) -> Result<u64> {
}
fn find_available_name(dir: &Path, file_name: &str, extension: &str) -> String {
lazy_static! {
static ref RESERVED_WINDOWS_FILENAMES: Regex = RegexBuilder::new(r#"^.*\.|(?:COM|CLOCK\$|CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\..*)?$"#)
static RESERVED_WINDOWS_FILENAMES: LazyLock<Regex> = LazyLock::new(|| {
RegexBuilder::new(r#"^.*\.|(?:COM|CLOCK\$|CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\..*)?$"#)
.case_insensitive(true)
.build()
.unwrap();
static ref COPY_COUNTER_PATTERN: Regex = RegexBuilder::new(r#"^(?<name>.*) \((?<count>\d*)\)$"#)
.unwrap()
});
static COPY_COUNTER_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
RegexBuilder::new(r#"^(?<name>.*) \((?<count>\d*)\)$"#)
.case_insensitive(true)
.unicode(true)
.build()
.unwrap();
}
.unwrap()
});
let mut file_name = file_name.replace(
[

View File

@@ -5,30 +5,27 @@ use crate::state::{
DirectoryInfo, ProfileInstallStage, ProjectType, attached_world_data,
};
use crate::worlds::WorldType;
use futures::{SinkExt, StreamExt, channel::mpsc::channel};
use notify::{RecommendedWatcher, RecursiveMode};
use notify_debouncer_mini::{DebounceEventResult, Debouncer, new_debouncer};
use std::time::Duration;
use tokio::sync::RwLock;
use tokio::sync::{RwLock, mpsc::channel};
pub type FileWatcher = RwLock<Debouncer<RecommendedWatcher>>;
pub async fn init_watcher() -> crate::Result<FileWatcher> {
let (mut tx, mut rx) = channel(1);
let (tx, mut rx) = channel(1);
let file_watcher = new_debouncer(
Duration::from_secs_f32(1.0),
move |res: DebounceEventResult| {
futures::executor::block_on(async {
tx.send(res).await.unwrap();
})
tx.blocking_send(res).ok();
},
)?;
tokio::task::spawn(async move {
let span = tracing::span!(tracing::Level::INFO, "init_watcher");
tracing::info!(parent: &span, "Initting watcher");
while let Some(res) = rx.next().await {
while let Some(res) = rx.recv().await {
let _span = span.enter();
match res {

View File

@@ -2,7 +2,6 @@ use crate::ErrorKind;
use crate::util::fetch::REQWEST_CLIENT;
use base64::Engine;
use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE_NO_PAD};
use byteorder::BigEndian;
use chrono::{DateTime, Duration, TimeZone, Utc};
use dashmap::DashMap;
use futures::TryStreamExt;
@@ -62,12 +61,6 @@ pub enum MinecraftAuthenticationError {
#[source]
source: reqwest::Error,
},
#[error("Error creating signed request buffer {step:?}: {source}")]
ConstructingSignedRequest {
step: MinecraftAuthStep,
#[source]
source: std::io::Error,
},
#[error("Error reading XBOX Session ID header")]
NoSessionId,
#[error("Error reading user hash")]
@@ -1087,56 +1080,25 @@ async fn send_signed_request<T: DeserializeOwned>(
let time: u128 =
{ ((current_date.timestamp() as u128) + 11644473600) * 10000000 };
use byteorder::WriteBytesExt;
let mut buffer = Vec::new();
buffer.write_u32::<BigEndian>(1).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
buffer.write_u8(0).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
buffer
.write_u64::<BigEndian>(time as u64)
.map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest {
source,
step,
}
})?;
buffer.write_u8(0).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
buffer.extend_from_slice(&1_u32.to_be_bytes()[..]);
buffer.push(0_u8);
buffer.extend_from_slice(&(time as u64).to_be_bytes()[..]);
buffer.push(0_u8);
buffer.extend_from_slice("POST".as_bytes());
buffer.write_u8(0).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
buffer.push(0_u8);
buffer.extend_from_slice(url_path.as_bytes());
buffer.write_u8(0).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
buffer.push(0_u8);
buffer.extend_from_slice(&auth);
buffer.write_u8(0).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
buffer.push(0_u8);
buffer.extend_from_slice(&body);
buffer.write_u8(0).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
buffer.push(0_u8);
let ecdsa_sig: Signature = key.key.sign(&buffer);
let mut sig_buffer = Vec::new();
sig_buffer.write_i32::<BigEndian>(1).map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest { source, step }
})?;
sig_buffer
.write_u64::<BigEndian>(time as u64)
.map_err(|source| {
MinecraftAuthenticationError::ConstructingSignedRequest {
source,
step,
}
})?;
sig_buffer.extend_from_slice(&1_i32.to_be_bytes()[..]);
sig_buffer.extend_from_slice(&(time as u64).to_be_bytes()[..]);
sig_buffer.extend_from_slice(&ecdsa_sig.r().to_bytes());
sig_buffer.extend_from_slice(&ecdsa_sig.s().to_bytes());

View File

@@ -9,7 +9,6 @@ use crate::util::fetch::{FetchSemaphore, IoSemaphore, write_cached_icon};
use crate::util::io::{self};
use chrono::{DateTime, TimeDelta, TimeZone, Utc};
use dashmap::DashMap;
use lazy_static::lazy_static;
use regex::Regex;
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
@@ -17,6 +16,7 @@ use std::collections::HashSet;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::path::Path;
use std::sync::LazyLock;
use tokio::fs::DirEntry;
use tokio::io::{AsyncBufReadExt, AsyncRead};
use tokio::task::JoinSet;
@@ -837,9 +837,9 @@ impl Profile {
state: &crate::State,
join_entry: &mut JoinLogEntry,
) -> crate::Result<()> {
lazy_static! {
static ref LOG_LINE_REGEX: Regex = Regex::new(r"^\[[0-9]{2}(?::[0-9]{2}){2}] \[.+?/[A-Z]+?]: Connecting to (.+?), ([1-9][0-9]{0,4})$").unwrap();
}
static LOG_LINE_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^\[[0-9]{2}(?::[0-9]{2}){2}] \[.+?/[A-Z]+?]: Connecting to (.+?), ([1-9][0-9]{0,4})$").unwrap()
});
let reader = tokio::io::BufReader::new(reader);
let mut lines = reader.lines();
while let Some(log_line) = lines.next_line().await? {

View File

@@ -1,14 +1,14 @@
//! Functions for fetching infromation from the Internet
//! Functions for fetching information from the Internet
use super::io::{self, IOError};
use crate::config::{MODRINTH_API_URL, MODRINTH_API_URL_V3};
use crate::event::LoadingBarId;
use crate::event::emit::emit_loading;
use bytes::Bytes;
use lazy_static::lazy_static;
use reqwest::Method;
use serde::de::DeserializeOwned;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use std::time::{self};
use tokio::sync::Semaphore;
use tokio::{fs::File, io::AsyncWriteExt};
@@ -18,22 +18,20 @@ pub struct IoSemaphore(pub Semaphore);
#[derive(Debug)]
pub struct FetchSemaphore(pub Semaphore);
lazy_static! {
pub static ref REQWEST_CLIENT: reqwest::Client = {
let mut headers = reqwest::header::HeaderMap::new();
let header = reqwest::header::HeaderValue::from_str(&format!(
"modrinth/theseus/{} (support@modrinth.com)",
env!("CARGO_PKG_VERSION")
))
.unwrap();
headers.insert(reqwest::header::USER_AGENT, header);
reqwest::Client::builder()
.tcp_keepalive(Some(time::Duration::from_secs(10)))
.default_headers(headers)
.build()
.expect("Reqwest Client Building Failed")
};
}
pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
let mut headers = reqwest::header::HeaderMap::new();
let header = reqwest::header::HeaderValue::from_str(&format!(
"modrinth/theseus/{} (support@modrinth.com)",
env!("CARGO_PKG_VERSION")
))
.unwrap();
headers.insert(reqwest::header::USER_AGENT, header);
reqwest::Client::builder()
.tcp_keepalive(Some(time::Duration::from_secs(10)))
.default_headers(headers)
.build()
.expect("Reqwest Client Building Failed")
});
const FETCH_ATTEMPTS: usize = 3;
#[tracing::instrument(skip(semaphore))]

View File

@@ -276,11 +276,10 @@ pub async fn check_java_at_filepath(path: &Path) -> Option<JavaVersion> {
};
let bytes = include_bytes!("../../library/JavaInfo.class");
let tempdir: PathBuf = tempfile::tempdir().ok()?.into_path();
if !tempdir.exists() {
let Ok(tempdir) = tempfile::tempdir() else {
return None;
}
let file_path = tempdir.join("JavaInfo.class");
};
let file_path = tempdir.path().join("JavaInfo.class");
io::write(&file_path, bytes).await.ok()?;
let output = Command::new(&java)

View File

@@ -1,6 +1,5 @@
//! Platform-related code
use daedalus::minecraft::{Os, OsRule};
use regex::Regex;
// OS detection
pub trait OsExt {
@@ -92,12 +91,16 @@ pub fn os_rule(
}
}
if let Some(version) = &rule.version {
if let Ok(regex) = Regex::new(version.as_str()) {
rule_match &=
regex.is_match(&sys_info::os_release().unwrap_or_default());
}
}
// `rule.version` is ignored because it's not usually seen on real recent
// Minecraft version manifests, its alleged regex syntax is undefined and is
// likely to not match `Regex`'s, and the way to get the value to match it
// against is allegedly calling `System.getProperty("os.version")`, which
// on Windows the OpenJDK implements by fetching the kernel32.dll version,
// an approach that no public Rust library implements. Moreover, launchers
// such as PrismLauncher also ignore this field. Code references:
// - https://github.com/openjdk/jdk/blob/948ade8e7003a41683600428c8e3155c7ed798db/src/java.base/windows/native/libjava/java_props_md.c#L556
// - https://github.com/PrismLauncher/PrismLauncher/blob/1c20faccf88999474af70db098a4c10e7a03af33/launcher/minecraft/Rule.h#L77
// - https://github.com/FillZpp/sys-info-rs/blob/60ecf1470a5b7c90242f429934a3bacb6023ec4d/c/windows.c#L23-L38
rule_match
}

View File

@@ -4,13 +4,12 @@ version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "2.0.12"
uuid = { version = "1.2.2", features = ["v4", "fast-rng", "serde"] }
serde_bytes = "0.11"
rand = "0.8.5"
either = "1.13"
chrono = { version = "0.4.26", features = ["serde"] }
serde_cbor = "0.11"
lazy_static = "1.5"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
thiserror.workspace = true
uuid = { workspace = true, features = ["v4", "fast-rng", "serde"] }
serde_bytes.workspace = true
rand.workspace = true
either.workspace = true
chrono = { workspace = true, features = ["serde"] }
serde_cbor.workspace = true

View File

@@ -1,8 +1,7 @@
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::{collections::HashMap, sync::LazyLock};
lazy_static! {
static ref SPECIAL_PARENTS: HashMap<&'static str, &'static str> = {
static SPECIAL_PARENTS: LazyLock<HashMap<&'static str, &'static str>> =
LazyLock::new(|| {
let mut m = HashMap::new();
m.insert("15w14a", "1.8.3");
m.insert("1.RV-Pre1", "1.9.2");
@@ -12,8 +11,7 @@ lazy_static! {
m.insert("23w13a_or_b", "23w13a");
m.insert("24w14potato", "24w12a");
m
};
}
});
pub fn is_feature_supported_in(
version: &str,

View File

@@ -14,7 +14,7 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
thiserror = "2.0"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
chrono = { workspace = true, features = ["serde"] }
thiserror.workspace = true