From 2f0ef07944d11463b46b194119e895d591c79dd3 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Wed, 19 Nov 2025 12:38:30 +0000 Subject: [PATCH 01/12] Add logging and change limit of Mural payouts task (#4798) --- apps/labrinth/src/background_task.rs | 3 ++- apps/labrinth/src/lib.rs | 6 +++--- apps/labrinth/src/queue/payouts/mural.rs | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/labrinth/src/background_task.rs b/apps/labrinth/src/background_task.rs index 3e1a8a33..ee60a987 100644 --- a/apps/labrinth/src/background_task.rs +++ b/apps/labrinth/src/background_task.rs @@ -197,7 +197,8 @@ pub async fn payouts( } pub async fn sync_payout_statuses(pool: sqlx::Pool, mural: MuralPay) { - const LIMIT: u32 = 1000; + // Mural sets a max limit of 100 for search payouts endpoint + const LIMIT: u32 = 100; info!("Started syncing payout statuses"); diff --git a/apps/labrinth/src/lib.rs b/apps/labrinth/src/lib.rs index a66e1426..9f7c0107 100644 --- a/apps/labrinth/src/lib.rs +++ b/apps/labrinth/src/lib.rs @@ -9,7 +9,7 @@ use queue::{ session::AuthQueue, socket::ActiveSockets, }; use sqlx::Postgres; -use tracing::{info, warn}; +use tracing::{debug, info, warn}; extern crate clickhouse as clickhouse_crate; use clickhouse_crate::Client; @@ -240,14 +240,14 @@ pub fn app_setup( let redis_ref = redis_ref.clone(); async move { - info!("Indexing analytics queue"); + debug!("Indexing analytics queue"); let result = analytics_queue_ref .index(client_ref, &redis_ref, &pool_ref) .await; if let Err(e) = result { warn!("Indexing analytics queue failed: {:?}", e); } - info!("Done indexing analytics queue"); + debug!("Done indexing analytics queue"); } }); } diff --git a/apps/labrinth/src/queue/payouts/mural.rs b/apps/labrinth/src/queue/payouts/mural.rs index a5f44d7c..6d1cb3da 100644 --- a/apps/labrinth/src/queue/payouts/mural.rs +++ b/apps/labrinth/src/queue/payouts/mural.rs @@ -6,7 +6,7 @@ use muralpay::{MuralError, MuralPay, TokenFeeRequest}; use rust_decimal::{Decimal, prelude::ToPrimitive}; use serde::{Deserialize, Serialize}; use sqlx::PgPool; -use tracing::warn; +use tracing::{info, warn}; use crate::{ database::models::DBPayoutId, @@ -271,6 +271,8 @@ pub async fn sync_pending_payouts_from_mural( status: PayoutStatus, } + info!("Syncing pending payouts from Mural"); + let mut txn = db .begin() .await @@ -299,6 +301,8 @@ pub async fn sync_pending_payouts_from_mural( .await .wrap_internal_err("failed to fetch incomplete Mural payouts")?; + info!("Found {} incomplete Mural payouts", rows.len()); + let futs = rows.into_iter().map(|row| async move { let platform_id = row.platform_id.wrap_err("no platform ID")?; let payout_request_id = platform_id.parse::() @@ -369,6 +373,8 @@ pub async fn sync_failed_mural_payouts_to_labrinth( mural: &MuralPay, limit: u32, ) -> eyre::Result<()> { + info!("Syncing failed Mural payouts to Labrinth"); + let mut next_id = None; loop { let search_resp = mural @@ -393,6 +399,11 @@ pub async fn sync_failed_mural_payouts_to_labrinth( break; } + info!( + "Found {} canceled or failed Mural payouts", + search_resp.results.len() + ); + let mut payout_platform_id = Vec::::new(); let mut payout_new_status = Vec::::new(); @@ -430,6 +441,11 @@ pub async fn sync_failed_mural_payouts_to_labrinth( .await .wrap_internal_err("failed to update payout statuses")?; + info!( + "Updated {} payouts in database from Mural info", + payout_platform_id.len() + ); + if next_id.is_none() { break; } From e59664426bbbb9852a87ee9807ed7a081edcd7b3 Mon Sep 17 00:00:00 2001 From: Prospector <6166773+Prospector@users.noreply.github.com> Date: Wed, 19 Nov 2025 15:17:01 -0800 Subject: [PATCH 02/12] changelog --- packages/utils/changelog.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/utils/changelog.ts b/packages/utils/changelog.ts index 44e0b5e4..0ec2301f 100644 --- a/packages/utils/changelog.ts +++ b/packages/utils/changelog.ts @@ -10,6 +10,16 @@ export type VersionEntry = { } const VERSIONS: VersionEntry[] = [ + { + date: `2025-11-19T15:15:00-08:00`, + product: 'app', + version: '0.10.20', + body: `## Improvements +- Improved contrast, visibility, and consistency of UI elements, especially in light mode. +- Fixed ads showing up in the loading screen, even when you have Modrinth+. +- Added a warning banner when Minecraft's authentication servers are detected to be down. +- Fixed icon when creating an instance not being saved.`, + }, { date: `2025-11-14T12:15:00-08:00`, product: 'servers', From 130c2863ab6d8b600504a093edccd91cd4eced68 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Fri, 21 Nov 2025 11:13:11 +0000 Subject: [PATCH 03/12] Fix exposing Docker Compose ports to broadcast addr (#4805) --- docker-compose.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 965599fd..9ded6c63 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: volumes: - db-data:/var/lib/postgresql/data ports: - - '5432:5432' + - '127.0.0.1:5432:5432' environment: POSTGRES_USER: labrinth POSTGRES_PASSWORD: labrinth @@ -21,7 +21,7 @@ services: container_name: labrinth-meilisearch restart: on-failure ports: - - '7700:7700' + - '127.0.0.1:7700:7700' volumes: - meilisearch-data:/data.ms environment: @@ -37,7 +37,7 @@ services: container_name: labrinth-redis restart: on-failure ports: - - '6379:6379' + - '127.0.0.1:6379:6379' volumes: - redis-data:/data healthcheck: @@ -49,7 +49,7 @@ services: image: clickhouse/clickhouse-server container_name: labrinth-clickhouse ports: - - '8123:8123' + - '127.0.0.1:8123:8123' environment: CLICKHOUSE_USER: default CLICKHOUSE_PASSWORD: default @@ -62,8 +62,8 @@ services: image: axllent/mailpit:v1.27 container_name: labrinth-mail ports: - - '1025:1025' - - '8025:8025' + - '127.0.0.1:1025:1025' + - '127.0.0.1:8025:8025' environment: MP_ENABLE_SPAMASSASSIN: postmark healthcheck: @@ -82,7 +82,7 @@ services: image: gotenberg/gotenberg:8 container_name: labrinth-gotenberg ports: - - '13000:3000' + - '127.0.0.1:13000:3000' extra_hosts: # Gotenberg must send a message on a webhook to our backend, # so it must have access to our local network @@ -95,7 +95,7 @@ services: dockerfile: ./apps/labrinth/Dockerfile container_name: labrinth ports: - - '8000:8000' + - '127.0.0.1:8000:8000' env_file: ./apps/labrinth/.env.docker-compose volumes: - labrinth-cdn-data:/tmp/modrinth From bcf14a4c5190a0b4b56f0e5453d1a7c9b382f02b Mon Sep 17 00:00:00 2001 From: aecsocket Date: Fri, 21 Nov 2025 22:45:34 +0000 Subject: [PATCH 04/12] Fix downloading libraries for Forge 1.7.2 (#4808) * wip: fix Forge 1.7.2 downloads * Bump recursion limit --- apps/app/src/main.rs | 1 + packages/app-lib/src/launcher/download.rs | 37 +++++++++++++++++------ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/apps/app/src/main.rs b/apps/app/src/main.rs index 731565dc..7047f1eb 100644 --- a/apps/app/src/main.rs +++ b/apps/app/src/main.rs @@ -2,6 +2,7 @@ all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" )] +#![recursion_limit = "256"] use native_dialog::{DialogBuilder, MessageLevel}; use std::env; diff --git a/packages/app-lib/src/launcher/download.rs b/packages/app-lib/src/launcher/download.rs index 5d911df5..72fd08e4 100644 --- a/packages/app-lib/src/launcher/download.rs +++ b/packages/app-lib/src/launcher/download.rs @@ -395,17 +395,36 @@ pub async fn download_libraries( .unwrap_or("https://libraries.minecraft.net/") ); - let bytes = - fetch(&url, None, &st.fetch_semaphore, &st.pool) - .await?; - - write(&path, &bytes, &st.io_semaphore).await?; - tracing::trace!( - "Fetched library {} to path {:?}", - &library.name, - &path + "Attempting to fetch {} from {url}", + library.name, ); + + // It's OK for this fetch to fail, since the URL might not even be valid. + // We're constructing a download URL basically out of thin air, and hoping + // that it's valid. Since PrismLauncher ignores the library (see above), a + // failed download here is not a fatal condition. + // + // See DEV-479. + match fetch(&url, None, &st.fetch_semaphore, &st.pool).await + { + Ok(bytes) => { + write(&path, &bytes, &st.io_semaphore).await?; + + tracing::debug!( + "Fetched library {} to path {:?}", + &library.name, + &path + ); + } + Err(err) => { + tracing::debug!( + "Failed to download library {} from {url} - \ + this is not necessarily an error: {err:#?}", + &library.name + ); + } + } } } From f333a752211a165190dfed7e194bed0a01c9c9ef Mon Sep 17 00:00:00 2001 From: Truman Gao <106889354+tdgao@users.noreply.github.com> Date: Sat, 22 Nov 2025 18:38:13 -0800 Subject: [PATCH 05/12] fix empty state for projects in "All" tab (#4801) Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> --- apps/frontend/src/pages/user/[id].vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/pages/user/[id].vue b/apps/frontend/src/pages/user/[id].vue index da7f8db8..04440525 100644 --- a/apps/frontend/src/pages/user/[id].vue +++ b/apps/frontend/src/pages/user/[id].vue @@ -304,7 +304,13 @@ /> -
+

From 31417a2aa1c319fe731b153049942ceff440c2bf Mon Sep 17 00:00:00 2001 From: aecsocket Date: Sun, 23 Nov 2025 20:48:15 +0000 Subject: [PATCH 06/12] more logging on sync payouts task (#4814) --- apps/labrinth/src/queue/payouts/mural.rs | 39 ++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/apps/labrinth/src/queue/payouts/mural.rs b/apps/labrinth/src/queue/payouts/mural.rs index 6d1cb3da..56552aa7 100644 --- a/apps/labrinth/src/queue/payouts/mural.rs +++ b/apps/labrinth/src/queue/payouts/mural.rs @@ -6,7 +6,7 @@ use muralpay::{MuralError, MuralPay, TokenFeeRequest}; use rust_decimal::{Decimal, prelude::ToPrimitive}; use serde::{Deserialize, Serialize}; use sqlx::PgPool; -use tracing::{info, warn}; +use tracing::{info, trace, warn}; use crate::{ database::models::DBPayoutId, @@ -399,13 +399,22 @@ pub async fn sync_failed_mural_payouts_to_labrinth( break; } + let num_canceled = search_resp + .results + .iter() + .filter(|p| p.status == muralpay::PayoutStatus::Canceled) + .count(); + let num_failed = search_resp + .results + .iter() + .filter(|p| p.status == muralpay::PayoutStatus::Failed) + .count(); info!( - "Found {} canceled or failed Mural payouts", - search_resp.results.len() + "Found {num_canceled} canceled and {num_failed} failed Mural payouts" ); - let mut payout_platform_id = Vec::::new(); - let mut payout_new_status = Vec::::new(); + let mut payout_platform_ids = Vec::::new(); + let mut payout_new_statuses = Vec::::new(); for payout_req in search_resp.results { let new_payout_status = match payout_req.status { @@ -419,12 +428,17 @@ pub async fn sync_failed_mural_payouts_to_labrinth( continue; } }; + let payout_platform_id = payout_req.id; - payout_platform_id.push(payout_req.id.to_string()); - payout_new_status.push(new_payout_status.to_string()); + trace!( + "- Payout {payout_platform_id} set to {new_payout_status:?}", + ); + + payout_platform_ids.push(payout_platform_id.to_string()); + payout_new_statuses.push(new_payout_status.to_string()); } - sqlx::query!( + let result = sqlx::query!( " UPDATE payouts SET status = u.status @@ -433,8 +447,8 @@ pub async fn sync_failed_mural_payouts_to_labrinth( payouts.method = $3 AND payouts.platform_id = u.platform_id ", - &payout_platform_id, - &payout_new_status, + &payout_platform_ids, + &payout_new_statuses, PayoutMethodType::MuralPay.as_str(), ) .execute(db) @@ -442,8 +456,9 @@ pub async fn sync_failed_mural_payouts_to_labrinth( .wrap_internal_err("failed to update payout statuses")?; info!( - "Updated {} payouts in database from Mural info", - payout_platform_id.len() + "Attempted to update {} payouts in database from Mural info, {} rows affected", + payout_platform_ids.len(), + result.rows_affected() ); if next_id.is_none() { From 0178fddc387613d7f90bd1f23db752c4e62f4373 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Mon, 24 Nov 2025 13:35:14 +0000 Subject: [PATCH 07/12] Install mod update dependencies automatically (#4800) * Redownload version dependencies when updating a mod * Fix update all button as well --- apps/app-frontend/src/pages/instance/Mods.vue | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/apps/app-frontend/src/pages/instance/Mods.vue b/apps/app-frontend/src/pages/instance/Mods.vue index 91b9f70f..a2c3c80e 100644 --- a/apps/app-frontend/src/pages/instance/Mods.vue +++ b/apps/app-frontend/src/pages/instance/Mods.vue @@ -301,11 +301,13 @@ import { get_organization_many, get_project_many, get_team_many, + get_version, get_version_many, } from '@/helpers/cache.js' import { profile_listener } from '@/helpers/events.js' import { add_project_from_path, + get, get_projects, remove_project, toggle_disable_project, @@ -314,6 +316,7 @@ import { } from '@/helpers/profile.js' import type { CacheBehaviour, ContentFile, GameInstance } from '@/helpers/types' import { highlightModInProfile } from '@/helpers/utils.js' +import { installVersionDependencies } from '@/store/install' const { handleError } = injectNotificationManager() @@ -627,10 +630,15 @@ const sortProjects = (filter: string) => { const updateAll = async () => { const setProjects = [] + const outdatedProjects = [] + for (const [i, project] of projects.value.entries()) { if (project.outdated) { project.updating = true setProjects.push(i) + if (project.updateVersion) { + outdatedProjects.push(project.updateVersion) + } } } @@ -646,6 +654,21 @@ const updateAll = async () => { projects.value[index].updateVersion = undefined } } + + if (outdatedProjects.length > 0) { + const profile = await get(props.instance.path).catch(handleError) + + if (profile) { + for (const versionId of outdatedProjects) { + const versionData = await get_version(versionId, 'must_revalidate').catch(handleError) + + if (versionData) { + await installVersionDependencies(profile, versionData).catch(handleError) + } + } + } + } + for (const project of setProjects) { projects.value[project].updating = false } @@ -662,6 +685,19 @@ const updateProject = async (mod: ProjectListEntry) => { mod.updating = true await new Promise((resolve) => setTimeout(resolve, 0)) mod.path = await update_project(props.instance.path, mod.path).catch(handleError) + + if (mod.updateVersion) { + const versionData = await get_version(mod.updateVersion, 'must_revalidate').catch(handleError) + + if (versionData) { + const profile = await get(props.instance.path).catch(handleError) + + if (profile) { + await installVersionDependencies(profile, versionData).catch(handleError) + } + } + } + mod.updating = false mod.outdated = false From b56f39ce07d8faa433f28a380e65f49cdb1d7ea5 Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Wed, 26 Nov 2025 23:29:29 +0000 Subject: [PATCH 08/12] fix: edit server icon issues (#4821) --- .../pages/servers/manage/[id]/options/index.vue | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/pages/servers/manage/[id]/options/index.vue b/apps/frontend/src/pages/servers/manage/[id]/options/index.vue index ec7e8047..9f228d4b 100644 --- a/apps/frontend/src/pages/servers/manage/[id]/options/index.vue +++ b/apps/frontend/src/pages/servers/manage/[id]/options/index.vue @@ -84,16 +84,19 @@ @change="uploadFile" /> - +
-
@@ -112,8 +115,8 @@