diff --git a/.sqlx/query-5c4262689205aafdd97a74bee0003f39eef0a34c97f97a939c14fb8fe349f7eb.json b/.sqlx/query-5c4262689205aafdd97a74bee0003f39eef0a34c97f97a939c14fb8fe349f7eb.json deleted file mode 100644 index 4fe0c3897..000000000 --- a/.sqlx/query-5c4262689205aafdd97a74bee0003f39eef0a34c97f97a939c14fb8fe349f7eb.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE files\n SET is_primary = TRUE\n WHERE (id = $1)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - }, - "hash": "5c4262689205aafdd97a74bee0003f39eef0a34c97f97a939c14fb8fe349f7eb" -} diff --git a/.sqlx/query-6a7b7704c2a0c52a70f5d881a1e6d3e8e77ddaa83ecc5688cd86bf327775fb76.json b/.sqlx/query-6a7b7704c2a0c52a70f5d881a1e6d3e8e77ddaa83ecc5688cd86bf327775fb76.json deleted file mode 100644 index fc2b4028d..000000000 --- a/.sqlx/query-6a7b7704c2a0c52a70f5d881a1e6d3e8e77ddaa83ecc5688cd86bf327775fb76.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT f.id id FROM hashes h\n INNER JOIN files f ON h.file_id = f.id\n WHERE h.algorithm = $2 AND h.hash = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Bytea", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "6a7b7704c2a0c52a70f5d881a1e6d3e8e77ddaa83ecc5688cd86bf327775fb76" -} diff --git a/.sqlx/query-6d883ea05aead20f571a0f63bfd63f1d432717ec7a0fb9ab29e01fcb061b3afc.json b/.sqlx/query-6d883ea05aead20f571a0f63bfd63f1d432717ec7a0fb9ab29e01fcb061b3afc.json deleted file mode 100644 index 55a5015c1..000000000 --- a/.sqlx/query-6d883ea05aead20f571a0f63bfd63f1d432717ec7a0fb9ab29e01fcb061b3afc.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE files\n SET is_primary = FALSE\n WHERE (version_id = $1)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - }, - "hash": "6d883ea05aead20f571a0f63bfd63f1d432717ec7a0fb9ab29e01fcb061b3afc" -} diff --git a/.sqlx/query-91d2ce7ee6a29a47a20655fef577c42f1cbb2f8de4d9ea8ab361fc210f08aa20.json b/.sqlx/query-91d2ce7ee6a29a47a20655fef577c42f1cbb2f8de4d9ea8ab361fc210f08aa20.json new file mode 100644 index 000000000..689e1c6c5 --- /dev/null +++ b/.sqlx/query-91d2ce7ee6a29a47a20655fef577c42f1cbb2f8de4d9ea8ab361fc210f08aa20.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT status FROM mods WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "status", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false + ] + }, + "hash": "91d2ce7ee6a29a47a20655fef577c42f1cbb2f8de4d9ea8ab361fc210f08aa20" +} diff --git a/src/routes/v2/version_creation.rs b/src/routes/v2/version_creation.rs index 9a634e064..f9422de21 100644 --- a/src/routes/v2/version_creation.rs +++ b/src/routes/v2/version_creation.rs @@ -7,6 +7,7 @@ use crate::models::projects::{ Dependency, FileType, Loader, ProjectId, Version, VersionId, VersionStatus, VersionType, }; use crate::models::v2::projects::LegacyVersion; +use crate::queue::moderation::AutomatedModerationQueue; use crate::queue::session::AuthQueue; use crate::routes::v3::project_creation::CreateError; use crate::routes::v3::version_creation; @@ -87,6 +88,7 @@ pub async fn version_create( redis: Data, file_host: Data>, session_queue: Data, + moderation_queue: Data, ) -> Result { let payload = v2_reroute::alter_actix_multipart( payload, @@ -233,6 +235,7 @@ pub async fn version_create( redis.clone(), file_host, session_queue, + moderation_queue, ) .await?; diff --git a/src/routes/v2/versions.rs b/src/routes/v2/versions.rs index 56aeb6a2e..ecf128635 100644 --- a/src/routes/v2/versions.rs +++ b/src/routes/v2/versions.rs @@ -211,7 +211,6 @@ pub struct EditVersion { pub game_versions: Option>, pub loaders: Option>, pub featured: Option, - pub primary_file: Option<(String, String)>, pub downloads: Option, pub status: Option, pub file_types: Option>, @@ -278,7 +277,6 @@ pub async fn version_edit( dependencies: new_version.dependencies, loaders, featured: new_version.featured, - primary_file: new_version.primary_file, downloads: new_version.downloads, status: new_version.status, file_types: new_version.file_types.map(|v| { diff --git a/src/routes/v3/project_creation.rs b/src/routes/v3/project_creation.rs index 536aaf386..da984f890 100644 --- a/src/routes/v3/project_creation.rs +++ b/src/routes/v3/project_creation.rs @@ -538,6 +538,11 @@ async fn project_create_inner( let version_data = project_create_data.initial_versions.get(index).unwrap(); // TODO: maybe redundant is this calculation done elsewhere? + let existing_file_names = created_version + .files + .iter() + .map(|x| x.filename.clone()) + .collect(); // Upload the new jar file super::version_creation::upload_file( &mut field, @@ -555,6 +560,7 @@ async fn project_create_inner( version_data.primary_file.is_some(), version_data.primary_file.as_deref() == Some(name), None, + existing_file_names, transaction, redis, ) diff --git a/src/routes/v3/version_creation.rs b/src/routes/v3/version_creation.rs index c8a3e7c64..db3defeb0 100644 --- a/src/routes/v3/version_creation.rs +++ b/src/routes/v3/version_creation.rs @@ -12,12 +12,13 @@ use crate::models::images::{Image, ImageContext, ImageId}; use crate::models::notifications::NotificationBody; use crate::models::pack::PackFileHash; use crate::models::pats::Scopes; -use crate::models::projects::{skip_nulls, DependencyType}; +use crate::models::projects::{skip_nulls, DependencyType, ProjectStatus}; use crate::models::projects::{ Dependency, FileType, Loader, ProjectId, Version, VersionFile, VersionId, VersionStatus, VersionType, }; use crate::models::teams::ProjectPermissions; +use crate::queue::moderation::AutomatedModerationQueue; use crate::queue::session::AuthQueue; use crate::util::routes::read_from_field; use crate::util::validate::validation_errors_to_string; @@ -102,6 +103,7 @@ pub async fn version_create( redis: Data, file_host: Data>, session_queue: Data, + moderation_queue: web::Data, ) -> Result { let mut transaction = client.begin().await?; let mut uploaded_files = Vec::new(); @@ -115,6 +117,7 @@ pub async fn version_create( &mut uploaded_files, &client, &session_queue, + &moderation_queue, ) .await; @@ -144,6 +147,7 @@ async fn version_create_inner( uploaded_files: &mut Vec, pool: &PgPool, session_queue: &AuthQueue, + moderation_queue: &AutomatedModerationQueue, ) -> Result { let cdn_url = dotenvy::var("CDN_URL")?; @@ -333,6 +337,8 @@ async fn version_create_inner( .clone() .ok_or_else(|| CreateError::InvalidInput("`data` field is required".to_string()))?; + let existing_file_names = version.files.iter().map(|x| x.filename.clone()).collect(); + upload_file( &mut field, file_host, @@ -349,6 +355,7 @@ async fn version_create_inner( version_data.primary_file.is_some(), version_data.primary_file.as_deref() == Some(name), version_data.file_types.get(name).copied().flatten(), + existing_file_names, transaction, redis, ) @@ -498,6 +505,19 @@ async fn version_create_inner( models::Project::clear_cache(project_id, None, Some(true), redis).await?; + let project_status = sqlx::query!( + "SELECT status FROM mods WHERE id = $1", + project_id as models::ProjectId, + ) + .fetch_optional(pool) + .await?; + + if let Some(project_status) = project_status { + if project_status.status == ProjectStatus::Processing.as_str() { + moderation_queue.projects.insert(project_id.into()); + } + } + Ok(HttpResponse::Ok().json(response)) } @@ -706,6 +726,7 @@ async fn upload_file_to_version_inner( true, false, file_data.file_types.get(name).copied().flatten(), + version.files.iter().map(|x| x.filename.clone()).collect(), transaction, &redis, ) @@ -759,11 +780,18 @@ pub async fn upload_file( ignore_primary: bool, force_primary: bool, file_type: Option, + other_file_names: Vec, transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, redis: &RedisPool, ) -> Result<(), CreateError> { let (file_name, file_extension) = get_name_ext(content_disposition)?; + if other_file_names.contains(&format!("{}.{}", file_name, file_extension)) { + return Err(CreateError::InvalidInput( + "Duplicate files are not allowed to be uploaded to Modrinth!".to_string(), + )); + } + if file_name.contains('/') { return Err(CreateError::InvalidInput( "File names must not contain slashes!".to_string(), diff --git a/src/routes/v3/versions.rs b/src/routes/v3/versions.rs index ab9697b98..91237b814 100644 --- a/src/routes/v3/versions.rs +++ b/src/routes/v3/versions.rs @@ -198,7 +198,6 @@ pub struct EditVersion { pub dependencies: Option>, pub loaders: Option>, pub featured: Option, - pub primary_file: Option<(String, String)>, pub downloads: Option, pub status: Option, pub file_types: Option>, @@ -490,48 +489,6 @@ pub async fn version_edit_helper( .await?; } - if let Some(primary_file) = &new_version.primary_file { - let result = sqlx::query!( - " - SELECT f.id id FROM hashes h - INNER JOIN files f ON h.file_id = f.id - WHERE h.algorithm = $2 AND h.hash = $1 - ", - primary_file.1.as_bytes(), - primary_file.0 - ) - .fetch_optional(&**pool) - .await? - .ok_or_else(|| { - ApiError::InvalidInput(format!( - "Specified file with hash {} does not exist.", - primary_file.1.clone() - )) - })?; - - sqlx::query!( - " - UPDATE files - SET is_primary = FALSE - WHERE (version_id = $1) - ", - id as database::models::ids::VersionId, - ) - .execute(&mut *transaction) - .await?; - - sqlx::query!( - " - UPDATE files - SET is_primary = TRUE - WHERE (id = $1) - ", - result.id, - ) - .execute(&mut *transaction) - .await?; - } - if let Some(body) = &new_version.changelog { sqlx::query!( " diff --git a/src/validate/modpack.rs b/src/validate/modpack.rs index 43711996f..cdabd699b 100644 --- a/src/validate/modpack.rs +++ b/src/validate/modpack.rs @@ -88,7 +88,11 @@ impl super::Validator for ModpackValidator { (x.ends_with("jar") || x.ends_with("zip")) && (x.starts_with("overrides/mods") || x.starts_with("client-overrides/mods") - || x.starts_with("server-overrides/mods")) + || x.starts_with("server-overrides/mods") + || x.starts_with("overrides/resourcepacks") + || x.starts_with("server-overrides/resourcepacks") + || x.starts_with("overrides/shaderpacks") + || x.starts_with("client-overrides/shaderpacks")) }) .flat_map(|x| x.rsplit('/').next().map(|x| x.to_string())) .collect::>(),