From 157647faf2778c74096e624aeef9cdb79539489c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sat, 24 May 2025 14:22:35 +0200 Subject: [PATCH 1/8] chore(labrinth): fix typos, simplify out `remove_duplicates` func --- apps/labrinth/src/models/v3/projects.rs | 26 +++++++++---------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/apps/labrinth/src/models/v3/projects.rs b/apps/labrinth/src/models/v3/projects.rs index 55631a6f6..0d2caa254 100644 --- a/apps/labrinth/src/models/v3/projects.rs +++ b/apps/labrinth/src/models/v3/projects.rs @@ -1,4 +1,6 @@ use std::collections::{HashMap, HashSet}; +use std::hash::RandomState; +use std::mem; use crate::database::models::loader_fields::VersionField; use crate::database::models::project_item::{LinkUrl, ProjectQueryResult}; @@ -8,6 +10,7 @@ use crate::models::ids::{ }; use ariadne::ids::UserId; use chrono::{DateTime, Utc}; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use validator::Validate; @@ -95,19 +98,6 @@ pub struct Project { pub fields: HashMap>, } -fn remove_duplicates(values: Vec) -> Vec { - let mut seen = HashSet::new(); - values - .into_iter() - .filter(|value| { - // Convert the JSON value to a string for comparison - let as_string = value.to_string(); - // Check if the string is already in the set - seen.insert(as_string) - }) - .collect() -} - // This is a helper function to convert a list of VersionFields into a HashMap of field name to vecs of values // This allows for removal of duplicates pub fn from_duplicate_version_fields( @@ -132,9 +122,11 @@ pub fn from_duplicate_version_fields( } } - // Remove duplicates by converting to string and back + // Remove duplicates for (_, v) in fields.iter_mut() { - *v = remove_duplicates(v.clone()); + *v = HashSet::<_, RandomState>::from_iter(mem::take(v).into_iter()) + .into_iter() + .collect_vec(); } fields } @@ -624,7 +616,7 @@ pub struct Version { pub downloads: u32, /// The type of the release - `Alpha`, `Beta`, or `Release`. pub version_type: VersionType, - /// The status of tne version + /// The status of the version pub status: VersionStatus, /// The requested status of the version (used for scheduling) pub requested_status: Option, @@ -880,7 +872,7 @@ impl std::fmt::Display for DependencyType { } impl DependencyType { - // These are constant, so this can remove unneccessary allocations (`to_string`) + // These are constant, so this can remove unnecessary allocations (`to_string`) pub fn as_str(&self) -> &'static str { match self { DependencyType::Required => "required", From 4abc0b6875f81fbbc5e610df847d8ba7347a509f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sat, 24 May 2025 14:39:09 +0200 Subject: [PATCH 2/8] fix(labrinth): implement `capitalize_first` so that it can't panic on wide chars --- apps/labrinth/src/routes/v2_reroute.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/labrinth/src/routes/v2_reroute.rs b/apps/labrinth/src/routes/v2_reroute.rs index a73baac4d..ed4de8025 100644 --- a/apps/labrinth/src/routes/v2_reroute.rs +++ b/apps/labrinth/src/routes/v2_reroute.rs @@ -264,11 +264,18 @@ pub fn convert_side_types_v2_bools( } pub fn capitalize_first(input: &str) -> String { - let mut result = input.to_owned(); - if let Some(first_char) = result.get_mut(0..1) { - first_char.make_ascii_uppercase(); - } - result + let mut first_char = true; + input + .chars() + .map(|char| { + if first_char { + first_char = false; + char.to_ascii_uppercase() + } else { + char + } + }) + .collect() } #[cfg(test)] From 4f7fa9dcec3383f34c04287f3e35c17eb2a4a0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sat, 24 May 2025 18:01:04 +0200 Subject: [PATCH 3/8] chore(labrinth): refactor out unneeded clone highlighted by nightly Clippy lints --- apps/labrinth/src/routes/v3/version_file.rs | 28 +++++++++------------ 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/apps/labrinth/src/routes/v3/version_file.rs b/apps/labrinth/src/routes/v3/version_file.rs index 744aa8d9b..4c7133221 100644 --- a/apps/labrinth/src/routes/v3/version_file.rs +++ b/apps/labrinth/src/routes/v3/version_file.rs @@ -52,10 +52,9 @@ pub async fn get_version_from_hash( .map(|x| x.1) .ok(); let hash = info.into_inner().0.to_lowercase(); - let algorithm = hash_query - .algorithm - .clone() - .unwrap_or_else(|| default_algorithm_from_hashes(&[hash.clone()])); + let algorithm = hash_query.algorithm.clone().unwrap_or_else(|| { + default_algorithm_from_hashes(std::slice::from_ref(&hash)) + }); let file = database::models::DBVersion::get_file_from_hash( algorithm, hash, @@ -140,10 +139,9 @@ pub async fn get_update_from_hash( .ok(); let hash = info.into_inner().0.to_lowercase(); if let Some(file) = database::models::DBVersion::get_file_from_hash( - hash_query - .algorithm - .clone() - .unwrap_or_else(|| default_algorithm_from_hashes(&[hash.clone()])), + hash_query.algorithm.clone().unwrap_or_else(|| { + default_algorithm_from_hashes(std::slice::from_ref(&hash)) + }), hash, hash_query.version_id.map(|x| x.into()), &**pool, @@ -577,10 +575,9 @@ pub async fn delete_file( .1; let hash = info.into_inner().0.to_lowercase(); - let algorithm = hash_query - .algorithm - .clone() - .unwrap_or_else(|| default_algorithm_from_hashes(&[hash.clone()])); + let algorithm = hash_query.algorithm.clone().unwrap_or_else(|| { + default_algorithm_from_hashes(std::slice::from_ref(&hash)) + }); let file = database::models::DBVersion::get_file_from_hash( algorithm.clone(), hash, @@ -709,10 +706,9 @@ pub async fn download_version( .ok(); let hash = info.into_inner().0.to_lowercase(); - let algorithm = hash_query - .algorithm - .clone() - .unwrap_or_else(|| default_algorithm_from_hashes(&[hash.clone()])); + let algorithm = hash_query.algorithm.clone().unwrap_or_else(|| { + default_algorithm_from_hashes(std::slice::from_ref(&hash)) + }); let file = database::models::DBVersion::get_file_from_hash( algorithm.clone(), hash, From 69826fbcf1b05eb2e085d3864204c8d16c344594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sat, 24 May 2025 18:51:03 +0200 Subject: [PATCH 4/8] chore(labrinth): simplify `capitalize_first` implementation --- apps/labrinth/src/routes/v2_reroute.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/labrinth/src/routes/v2_reroute.rs b/apps/labrinth/src/routes/v2_reroute.rs index ed4de8025..b6a193757 100644 --- a/apps/labrinth/src/routes/v2_reroute.rs +++ b/apps/labrinth/src/routes/v2_reroute.rs @@ -264,17 +264,10 @@ pub fn convert_side_types_v2_bools( } pub fn capitalize_first(input: &str) -> String { - let mut first_char = true; input .chars() - .map(|char| { - if first_char { - first_char = false; - char.to_ascii_uppercase() - } else { - char - } - }) + .enumerate() + .map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c }) .collect() } From 61c75c78e11ebc97524e6a5a57a8456bda6840c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sat, 24 May 2025 18:59:26 +0200 Subject: [PATCH 5/8] fix(labrinth): preserve ordering when deduplicating project field values This addresses an unintended behavior change on 157647faf2778c74096e624aeef9cdb79539489c. --- apps/labrinth/src/models/v3/projects.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/labrinth/src/models/v3/projects.rs b/apps/labrinth/src/models/v3/projects.rs index 0d2caa254..6e9f17cf2 100644 --- a/apps/labrinth/src/models/v3/projects.rs +++ b/apps/labrinth/src/models/v3/projects.rs @@ -1,5 +1,4 @@ -use std::collections::{HashMap, HashSet}; -use std::hash::RandomState; +use std::collections::HashMap; use std::mem; use crate::database::models::loader_fields::VersionField; @@ -124,9 +123,7 @@ pub fn from_duplicate_version_fields( // Remove duplicates for (_, v) in fields.iter_mut() { - *v = HashSet::<_, RandomState>::from_iter(mem::take(v).into_iter()) - .into_iter() - .collect_vec(); + *v = mem::take(v).into_iter().unique().collect_vec(); } fields } From a358bbc1b996bf4e67febc2391454b2e611e64b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sat, 24 May 2025 23:04:23 +0200 Subject: [PATCH 6/8] fix(labrinth/tests): make `index_swaps` test run successfully I wonder why we don't run these more often... --- apps/labrinth/tests/search.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/labrinth/tests/search.rs b/apps/labrinth/tests/search.rs index e05b13def..e8562f5c7 100644 --- a/apps/labrinth/tests/search.rs +++ b/apps/labrinth/tests/search.rs @@ -151,7 +151,7 @@ async fn index_swaps() { test_env.api.remove_project("alpha", USER_USER_PAT).await; assert_status!(&resp, StatusCode::NO_CONTENT); - // We should not get any results, because the project has been deleted + // Deletions should not be indexed immediately let projects = test_env .api .search_deserialized( @@ -160,7 +160,8 @@ async fn index_swaps() { USER_USER_PAT, ) .await; - assert_eq!(projects.total_hits, 0); + assert_eq!(projects.total_hits, 1); + assert!(projects.hits[0].slug.as_ref().unwrap().contains("alpha")); // But when we reindex, it should be gone let resp = test_env.api.reset_search_index().await; From 83f655673f4826eddd2d6887baa486c4ab27a3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sun, 25 May 2025 17:56:20 +0200 Subject: [PATCH 7/8] refactor: rename `.env.example` files to `.env.local`, make local envs more consistent between frontend and backend --- apps/daedalus_client/{.env.example => .env.local} | 0 apps/docs/src/content/docs/contributing/labrinth.md | 2 +- apps/frontend/.env.example | 3 --- apps/frontend/.env.local | 5 +++++ apps/labrinth/{.env.example => .env.local} | 2 +- packages/app-lib/{.env.example => .env.local} | 0 6 files changed, 7 insertions(+), 5 deletions(-) rename apps/daedalus_client/{.env.example => .env.local} (100%) delete mode 100644 apps/frontend/.env.example create mode 100644 apps/frontend/.env.local rename apps/labrinth/{.env.example => .env.local} (98%) rename packages/app-lib/{.env.example => .env.local} (100%) diff --git a/apps/daedalus_client/.env.example b/apps/daedalus_client/.env.local similarity index 100% rename from apps/daedalus_client/.env.example rename to apps/daedalus_client/.env.local diff --git a/apps/docs/src/content/docs/contributing/labrinth.md b/apps/docs/src/content/docs/contributing/labrinth.md index 8ce0ac881..155e854cb 100644 --- a/apps/docs/src/content/docs/contributing/labrinth.md +++ b/apps/docs/src/content/docs/contributing/labrinth.md @@ -7,7 +7,7 @@ This project is part of our [monorepo](https://github.com/modrinth/code). You ca [labrinth] is the Rust-based backend serving Modrinth's API with the help of the [Actix](https://actix.rs) framework. To get started with a labrinth instance, install docker, docker-compose (which comes with Docker), and [Rust]. The initial startup can be done simply with the command `docker-compose up`, or with `docker compose up` (Compose V2 and later). That will deploy a PostgreSQL database on port 5432 and a MeiliSearch instance on port 7700. To run the API itself, you'll need to use the `cargo run` command, this will deploy the API on port 8000. -To get a basic configuration, copy the `.env.example` file to `.env`. Now, you'll have to install the sqlx CLI, which can be done with cargo: +To get a basic configuration, copy the `.env.local` file to `.env`. Now, you'll have to install the sqlx CLI, which can be done with cargo: ```bash cargo install --git https://github.com/launchbadge/sqlx sqlx-cli --no-default-features --features postgres,rustls diff --git a/apps/frontend/.env.example b/apps/frontend/.env.example deleted file mode 100644 index 43ceb1d53..000000000 --- a/apps/frontend/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -BASE_URL=https://api.modrinth.com/v2/ -BROWSER_BASE_URL=https://api.modrinth.com/v2/ -PYRO_BASE_URL=https://archon.modrinth.com/ diff --git a/apps/frontend/.env.local b/apps/frontend/.env.local new file mode 100644 index 000000000..f764f8513 --- /dev/null +++ b/apps/frontend/.env.local @@ -0,0 +1,5 @@ +BASE_URL=http://127.0.0.1:8000/v2/ +BROWSER_BASE_URL=http://127.0.0.1:8000/v2/ +PYRO_BASE_URL=https://staging-archon.modrinth.com +PROD_OVERRIDE=true + diff --git a/apps/labrinth/.env.example b/apps/labrinth/.env.local similarity index 98% rename from apps/labrinth/.env.example rename to apps/labrinth/.env.local index 1fbb8eebd..203a41812 100644 --- a/apps/labrinth/.env.example +++ b/apps/labrinth/.env.local @@ -2,7 +2,7 @@ DEBUG=true RUST_LOG=info,sqlx::query=warn SENTRY_DSN=none -SITE_URL=https://modrinth.com +SITE_URL=http://localhost:3000 CDN_URL=https://staging-cdn.modrinth.com LABRINTH_ADMIN_KEY=feedbeef RATE_LIMIT_IGNORE_KEY=feedbeef diff --git a/packages/app-lib/.env.example b/packages/app-lib/.env.local similarity index 100% rename from packages/app-lib/.env.example rename to packages/app-lib/.env.local From 9e34ed9fa9f1d73d1e0becfb15b34e6b0535e3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sun, 25 May 2025 21:42:31 +0200 Subject: [PATCH 8/8] chore(labrinth/.env.local): proper email verif. and password reset paths --- apps/labrinth/.env.local | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/labrinth/.env.local b/apps/labrinth/.env.local index 203a41812..8675bcd69 100644 --- a/apps/labrinth/.env.local +++ b/apps/labrinth/.env.local @@ -87,8 +87,8 @@ SMTP_HOST=none SMTP_PORT=465 SMTP_TLS=tls -SITE_VERIFY_EMAIL_PATH=none -SITE_RESET_PASSWORD_PATH=none +SITE_VERIFY_EMAIL_PATH=auth/verify-email +SITE_RESET_PASSWORD_PATH=auth/reset-password SITE_BILLING_PATH=none SENDY_URL=none