mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 09:04:55 +00:00
feat: malware warning modal changes (#6721)
* feat: improved warning modals * fix: qa * feat: install to play and update to play changes * fix: dont warn for server projects as already reviewed * fix: lint
This commit is contained in:
@@ -26,7 +26,7 @@ pub use self::paths::{get_full_path, get_mod_full_path};
|
||||
pub use self::projects::{
|
||||
InstallProjectWithDependenciesRequest, add_project_from_path,
|
||||
add_project_from_version, install_project_with_dependencies,
|
||||
remove_project, repair_managed_modrinth,
|
||||
is_file_on_modrinth, remove_project, repair_managed_modrinth,
|
||||
switch_project_version_with_dependencies, toggle_disable_project,
|
||||
update_all_projects, update_managed_modrinth_version, update_project,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::event::emit::{emit_instance, emit_loading, init_loading};
|
||||
use crate::event::{InstancePayloadType, LoadingBarType};
|
||||
use crate::state::instances::adapters::sqlite::instance_rows;
|
||||
use crate::state::{ProjectType, State};
|
||||
use crate::state::{CacheBehaviour, CachedEntry, ProjectType, State};
|
||||
use crate::util::fetch;
|
||||
use modrinth_content_management::{
|
||||
ContentType, ResolutionPreferences, ResolveContentPlan,
|
||||
@@ -213,6 +213,21 @@ pub async fn add_project_from_path(
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn is_file_on_modrinth(path: &Path) -> crate::Result<bool> {
|
||||
let state = State::get().await?;
|
||||
let (_, hash) = fetch::sha1_file_async(path).await?;
|
||||
let files = CachedEntry::get_file_many(
|
||||
&[&hash],
|
||||
Some(CacheBehaviour::Bypass),
|
||||
&state.pool,
|
||||
&state.api_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(!files.is_empty())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn toggle_disable_project(
|
||||
instance_id: &str,
|
||||
|
||||
@@ -113,7 +113,8 @@ pub struct CreatePackInstance {
|
||||
pub icon: Option<PathBuf>, // the icon for the instance
|
||||
pub icon_url: Option<String>, // the URL icon for an instance during import
|
||||
pub link: Option<InstanceLink>,
|
||||
pub unknown_file: bool, // true when pack file isn't found on Modrinth via hash lookup
|
||||
pub unknown_file: bool, // true when the mrpack archive isn't found on Modrinth via hash lookup
|
||||
pub external_files_in_modpack: Vec<String>,
|
||||
pub skip_install_profile: Option<bool>,
|
||||
pub no_watch: Option<bool>,
|
||||
}
|
||||
@@ -130,6 +131,7 @@ impl Default for CreatePackInstance {
|
||||
icon_url: None,
|
||||
link: None,
|
||||
unknown_file: false,
|
||||
external_files_in_modpack: Vec::new(),
|
||||
skip_install_profile: Some(true),
|
||||
no_watch: Some(false),
|
||||
}
|
||||
@@ -149,7 +151,6 @@ pub struct CreatePack {
|
||||
pub description: CreatePackDescription,
|
||||
}
|
||||
|
||||
// The hash lookup only gates the unknown-pack warning, so avoid a long blocking scan for huge local packs.
|
||||
const MAX_LOCAL_FILE_HASH_LOOKUP_SIZE: u64 = 1024 * 1024 * 1024;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -214,9 +215,16 @@ pub async fn get_instance_from_pack(
|
||||
false
|
||||
};
|
||||
|
||||
let external_files_in_modpack =
|
||||
super::install_mrpack::get_external_files_from_mrpack(
|
||||
&CreatePackFile::Path(path),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(CreatePackInstance {
|
||||
name: file_name,
|
||||
unknown_file: !is_known_file,
|
||||
external_files_in_modpack,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use async_zip::base::read::{WithEntry, ZipEntryReader};
|
||||
use async_zip::tokio::read::fs::ZipFileReader as FsZipFileReader;
|
||||
use futures::StreamExt;
|
||||
use path_util::SafeRelativeUtf8UnixPathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{
|
||||
@@ -237,6 +237,96 @@ where
|
||||
Ok((size, hasher.digest().to_string()))
|
||||
}
|
||||
|
||||
pub(crate) async fn get_external_files_from_mrpack(
|
||||
file: &CreatePackFile,
|
||||
) -> crate::Result<Vec<String>> {
|
||||
let mut zip_reader = MrpackZipReader::new(file).await?;
|
||||
let Some(manifest_idx) =
|
||||
zip_reader.file().entries().iter().position(|entry| {
|
||||
matches!(entry.filename().as_str(), Ok("modrinth.index.json"))
|
||||
})
|
||||
else {
|
||||
return Err(crate::Error::from(crate::ErrorKind::InputError(
|
||||
"No pack manifest found in mrpack".to_string(),
|
||||
)));
|
||||
};
|
||||
|
||||
let manifest = zip_reader.read_entry_to_string(manifest_idx).await?;
|
||||
let pack: PackFormat = serde_json::from_str(&manifest)?;
|
||||
let mut candidates = pack
|
||||
.files
|
||||
.into_iter()
|
||||
.filter_map(|file| {
|
||||
let path = file.path.as_str();
|
||||
let hash = file.hashes.get(&PackFileHash::Sha1)?.clone();
|
||||
let file_name = path.rsplit('/').next()?.to_string();
|
||||
Some((file_name, hash))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let override_entries = zip_reader
|
||||
.file()
|
||||
.entries()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, entry)| {
|
||||
let path = entry.filename().as_str().ok()?;
|
||||
let relative_path = path
|
||||
.strip_prefix("overrides/")
|
||||
.or_else(|| path.strip_prefix("client-overrides/"))?;
|
||||
if path.ends_with('/')
|
||||
|| ProjectType::get_from_parent_folder(relative_path).is_none()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let file_name = relative_path.rsplit('/').next()?.to_string();
|
||||
Some((index, file_name))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (index, file_name) in override_entries {
|
||||
let (_, hash) = zip_reader.hash_entry(index).await?;
|
||||
candidates.push((file_name, hash));
|
||||
}
|
||||
|
||||
if candidates.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let state = State::get().await?;
|
||||
let hashes = candidates
|
||||
.iter()
|
||||
.map(|(_, hash)| hash.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
let recognized_hashes = match CachedEntry::get_file_many(
|
||||
&hashes,
|
||||
None,
|
||||
&state.pool,
|
||||
&state.api_semaphore,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(files) => files
|
||||
.into_iter()
|
||||
.map(|file| file.hash)
|
||||
.collect::<HashSet<_>>(),
|
||||
Err(err) => {
|
||||
tracing::warn!("Failed to look up files in imported mrpack: {err}");
|
||||
HashSet::new()
|
||||
}
|
||||
};
|
||||
|
||||
let mut external_files = candidates
|
||||
.into_iter()
|
||||
.filter_map(|(file_name, hash)| {
|
||||
(!recognized_hashes.contains(&hash)).then_some(file_name)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
external_files.sort();
|
||||
external_files.dedup();
|
||||
Ok(external_files)
|
||||
}
|
||||
|
||||
async fn extract_zip_entry<R>(
|
||||
mut reader: ZipEntryReader<'_, R, WithEntry<'_>>,
|
||||
path: &Path,
|
||||
|
||||
Reference in New Issue
Block a user