feat: instances v2 (#6431)

* feat: base of instances v2

* feat: use old profiles with compat layer

* prototype: instances v2

* fix: install_from using profile

* fix: skins migration fix

* fix: frontend still using profile path

* fix: add update proj multiselect guard

* fix: cargo fmt

* fix: content missing fields

* feat: break up app-lib/api/instance.rs

* fix: check_content_updates mismatch

* fix: updater modal cleanup w/new structure

* feat: better update all handling

* fix: remove preview_update_all

* fix: feedback on bulk update + lint

* fix: rem transitions

* fix: change to jsonb

* feat: app db backup after update

* fix: lint

* fix: sqlx prepare + use sqlx macros

* fix: lint

* fix: bugs

* feat: defuck the installing process up

* fix: bug of hell

* fix: shear

* fix: fmt

* fix: install progress spacing + change mc/content/overrides to bytes

* fix: lint

* fix: prepr

* fix: navtabs anim not working in app

* fix: worlds.vue improvements + browse page fixes

* feat: optimise queries + adapter fns

* fix: lint

* fix: lint

* feat: shared modrinth-content-management crate (#6469)

* feat: disable warnings setting

* feat: add instances shortcuts (#6329)

* Add modrinth://launch deep link to start a profile

Support external profile launching via modrinth://launch/{profile_path} for integrations such as Stream Deck.

* Change route to /launch/profile/{id} for future extensibility

* fix: ensure profile path is url decoded

* fix: URL-decode profile path from deep link

* fix: use urlencoding crate for URL decoding

* feat: implement app instance shortcuts

* feat: change windows shortcut creation to use windows api instead

* feat: implement creating a shortcut launching world/server

* format

* fmt

* fix multiline inline tables

* pnpm prepr

* feat: move create shortcut to last item

* refactor: split up shortcuts.rs for individual platforms

* refactor: turn profile launch url into url type

* use string literal and add safety comment

* pt2

* refactor: rename anything that's profile into instance

* update mac shortcut

---------

Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com>

---------

Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com>
Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com>
This commit is contained in:
Calum H.
2026-06-25 21:19:29 +00:00
committed by GitHub
co-authored by DJCheesusReal Truman Gao
parent ef4044534f
commit 734720e11e
353 changed files with 24745 additions and 9771 deletions
+88 -7
View File
@@ -12,7 +12,9 @@ use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::ffi::OsStr;
use std::future::Future;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::LazyLock;
use std::time::{self};
use tokio::sync::Semaphore;
@@ -207,6 +209,13 @@ pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
const FETCH_ATTEMPTS: usize = 2;
pub type FetchProgressFn<'a> = dyn FnMut(
u64,
u64,
) -> Pin<Box<dyn Future<Output = crate::Result<()>> + Send + 'a>>
+ Send
+ 'a;
#[tracing::instrument(skip(semaphore))]
pub async fn fetch(
url: &str,
@@ -311,6 +320,38 @@ pub async fn fetch_advanced(
.await
}
#[tracing::instrument(skip(json_body, semaphore, progress))]
#[allow(clippy::too_many_arguments)]
pub async fn fetch_advanced_with_progress(
method: Method,
url: &str,
sha1: Option<&str>,
json_body: Option<serde_json::Value>,
header: Option<(&str, &str)>,
download_meta: Option<&DownloadMeta>,
loading_bar: Option<(&LoadingBarId, f64)>,
uri_path: Option<&'static str>,
semaphore: &FetchSemaphore,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
progress: Option<&mut FetchProgressFn<'_>>,
) -> crate::Result<Bytes> {
fetch_advanced_with_client_and_progress(
method,
url,
sha1,
json_body,
header,
download_meta,
loading_bar,
uri_path,
semaphore,
exec,
&INSECURE_REQWEST_CLIENT,
progress,
)
.await
}
/// Downloads a file with retry and checksum functionality
#[tracing::instrument(skip(json_body, semaphore))]
#[allow(clippy::too_many_arguments)]
@@ -326,6 +367,39 @@ pub async fn fetch_advanced_with_client(
semaphore: &FetchSemaphore,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
client: &reqwest::Client,
) -> crate::Result<Bytes> {
fetch_advanced_with_client_and_progress(
method,
url,
sha1,
json_body,
header,
download_meta,
loading_bar,
uri_path,
semaphore,
exec,
client,
None,
)
.await
}
#[tracing::instrument(skip(json_body, semaphore, client, progress))]
#[allow(clippy::too_many_arguments)]
async fn fetch_advanced_with_client_and_progress(
method: Method,
url: &str,
sha1: Option<&str>,
json_body: Option<serde_json::Value>,
header: Option<(&str, &str)>,
download_meta: Option<&DownloadMeta>,
loading_bar: Option<(&LoadingBarId, f64)>,
uri_path: Option<&'static str>,
semaphore: &FetchSemaphore,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
client: &reqwest::Client,
mut progress: Option<&mut FetchProgressFn<'_>>,
) -> crate::Result<Bytes> {
let _permit = semaphore.0.acquire().await?;
@@ -398,23 +472,30 @@ pub async fn fetch_advanced_with_client(
return Err(backup_error.into());
}
let bytes = if let Some((bar, total)) = &loading_bar {
let bytes = if loading_bar.is_some() || progress.is_some() {
let length = resp.content_length();
if let Some(total_size) = length {
use futures::StreamExt;
let mut stream = resp.bytes_stream();
let mut bytes = Vec::new();
let mut downloaded = 0_u64;
while let Some(item) = stream.next().await {
let chunk = item.or(Err(ErrorKind::NoValueFor(
"fetch bytes".to_string(),
)))?;
downloaded += chunk.len() as u64;
bytes.append(&mut chunk.to_vec());
emit_loading(
bar,
(chunk.len() as f64 / total_size as f64)
* total,
None,
)?;
if let Some((bar, total)) = &loading_bar {
emit_loading(
bar,
(chunk.len() as f64 / total_size as f64)
* total,
None,
)?;
}
if let Some(progress) = progress.as_mut() {
progress(downloaded, total_size).await?;
}
}
Ok(bytes::Bytes::from(bytes))
-12
View File
@@ -314,18 +314,6 @@ pub async fn remove_file(
})
}
pub async fn open_file(
path: impl AsRef<std::path::Path>,
) -> Result<tokio::fs::File, IOError> {
let path = path.as_ref();
tokio::fs::File::open(path)
.await
.map_err(|e| IOError::IOPathError {
source: e,
path: path.to_string_lossy().to_string(),
})
}
pub async fn remove_dir(
path: impl AsRef<std::path::Path>,
) -> Result<(), IOError> {