You've already forked AstralRinth
forked from didirus/AstralRinth
Fix duplicate file names (#927)
* Fix duplicate file names * Fix checks
This commit is contained in:
@@ -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"
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
22
.sqlx/query-91d2ce7ee6a29a47a20655fef577c42f1cbb2f8de4d9ea8ab361fc210f08aa20.json
generated
Normal file
22
.sqlx/query-91d2ce7ee6a29a47a20655fef577c42f1cbb2f8de4d9ea8ab361fc210f08aa20.json
generated
Normal file
@@ -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"
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ use crate::models::projects::{
|
|||||||
Dependency, FileType, Loader, ProjectId, Version, VersionId, VersionStatus, VersionType,
|
Dependency, FileType, Loader, ProjectId, Version, VersionId, VersionStatus, VersionType,
|
||||||
};
|
};
|
||||||
use crate::models::v2::projects::LegacyVersion;
|
use crate::models::v2::projects::LegacyVersion;
|
||||||
|
use crate::queue::moderation::AutomatedModerationQueue;
|
||||||
use crate::queue::session::AuthQueue;
|
use crate::queue::session::AuthQueue;
|
||||||
use crate::routes::v3::project_creation::CreateError;
|
use crate::routes::v3::project_creation::CreateError;
|
||||||
use crate::routes::v3::version_creation;
|
use crate::routes::v3::version_creation;
|
||||||
@@ -87,6 +88,7 @@ pub async fn version_create(
|
|||||||
redis: Data<RedisPool>,
|
redis: Data<RedisPool>,
|
||||||
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
||||||
session_queue: Data<AuthQueue>,
|
session_queue: Data<AuthQueue>,
|
||||||
|
moderation_queue: Data<AutomatedModerationQueue>,
|
||||||
) -> Result<HttpResponse, CreateError> {
|
) -> Result<HttpResponse, CreateError> {
|
||||||
let payload = v2_reroute::alter_actix_multipart(
|
let payload = v2_reroute::alter_actix_multipart(
|
||||||
payload,
|
payload,
|
||||||
@@ -233,6 +235,7 @@ pub async fn version_create(
|
|||||||
redis.clone(),
|
redis.clone(),
|
||||||
file_host,
|
file_host,
|
||||||
session_queue,
|
session_queue,
|
||||||
|
moderation_queue,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -211,7 +211,6 @@ pub struct EditVersion {
|
|||||||
pub game_versions: Option<Vec<String>>,
|
pub game_versions: Option<Vec<String>>,
|
||||||
pub loaders: Option<Vec<models::projects::Loader>>,
|
pub loaders: Option<Vec<models::projects::Loader>>,
|
||||||
pub featured: Option<bool>,
|
pub featured: Option<bool>,
|
||||||
pub primary_file: Option<(String, String)>,
|
|
||||||
pub downloads: Option<u32>,
|
pub downloads: Option<u32>,
|
||||||
pub status: Option<VersionStatus>,
|
pub status: Option<VersionStatus>,
|
||||||
pub file_types: Option<Vec<EditVersionFileType>>,
|
pub file_types: Option<Vec<EditVersionFileType>>,
|
||||||
@@ -278,7 +277,6 @@ pub async fn version_edit(
|
|||||||
dependencies: new_version.dependencies,
|
dependencies: new_version.dependencies,
|
||||||
loaders,
|
loaders,
|
||||||
featured: new_version.featured,
|
featured: new_version.featured,
|
||||||
primary_file: new_version.primary_file,
|
|
||||||
downloads: new_version.downloads,
|
downloads: new_version.downloads,
|
||||||
status: new_version.status,
|
status: new_version.status,
|
||||||
file_types: new_version.file_types.map(|v| {
|
file_types: new_version.file_types.map(|v| {
|
||||||
|
|||||||
@@ -538,6 +538,11 @@ async fn project_create_inner(
|
|||||||
let version_data = project_create_data.initial_versions.get(index).unwrap();
|
let version_data = project_create_data.initial_versions.get(index).unwrap();
|
||||||
// TODO: maybe redundant is this calculation done elsewhere?
|
// 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
|
// Upload the new jar file
|
||||||
super::version_creation::upload_file(
|
super::version_creation::upload_file(
|
||||||
&mut field,
|
&mut field,
|
||||||
@@ -555,6 +560,7 @@ async fn project_create_inner(
|
|||||||
version_data.primary_file.is_some(),
|
version_data.primary_file.is_some(),
|
||||||
version_data.primary_file.as_deref() == Some(name),
|
version_data.primary_file.as_deref() == Some(name),
|
||||||
None,
|
None,
|
||||||
|
existing_file_names,
|
||||||
transaction,
|
transaction,
|
||||||
redis,
|
redis,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,12 +12,13 @@ use crate::models::images::{Image, ImageContext, ImageId};
|
|||||||
use crate::models::notifications::NotificationBody;
|
use crate::models::notifications::NotificationBody;
|
||||||
use crate::models::pack::PackFileHash;
|
use crate::models::pack::PackFileHash;
|
||||||
use crate::models::pats::Scopes;
|
use crate::models::pats::Scopes;
|
||||||
use crate::models::projects::{skip_nulls, DependencyType};
|
use crate::models::projects::{skip_nulls, DependencyType, ProjectStatus};
|
||||||
use crate::models::projects::{
|
use crate::models::projects::{
|
||||||
Dependency, FileType, Loader, ProjectId, Version, VersionFile, VersionId, VersionStatus,
|
Dependency, FileType, Loader, ProjectId, Version, VersionFile, VersionId, VersionStatus,
|
||||||
VersionType,
|
VersionType,
|
||||||
};
|
};
|
||||||
use crate::models::teams::ProjectPermissions;
|
use crate::models::teams::ProjectPermissions;
|
||||||
|
use crate::queue::moderation::AutomatedModerationQueue;
|
||||||
use crate::queue::session::AuthQueue;
|
use crate::queue::session::AuthQueue;
|
||||||
use crate::util::routes::read_from_field;
|
use crate::util::routes::read_from_field;
|
||||||
use crate::util::validate::validation_errors_to_string;
|
use crate::util::validate::validation_errors_to_string;
|
||||||
@@ -102,6 +103,7 @@ pub async fn version_create(
|
|||||||
redis: Data<RedisPool>,
|
redis: Data<RedisPool>,
|
||||||
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
||||||
session_queue: Data<AuthQueue>,
|
session_queue: Data<AuthQueue>,
|
||||||
|
moderation_queue: web::Data<AutomatedModerationQueue>,
|
||||||
) -> Result<HttpResponse, CreateError> {
|
) -> Result<HttpResponse, CreateError> {
|
||||||
let mut transaction = client.begin().await?;
|
let mut transaction = client.begin().await?;
|
||||||
let mut uploaded_files = Vec::new();
|
let mut uploaded_files = Vec::new();
|
||||||
@@ -115,6 +117,7 @@ pub async fn version_create(
|
|||||||
&mut uploaded_files,
|
&mut uploaded_files,
|
||||||
&client,
|
&client,
|
||||||
&session_queue,
|
&session_queue,
|
||||||
|
&moderation_queue,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -144,6 +147,7 @@ async fn version_create_inner(
|
|||||||
uploaded_files: &mut Vec<UploadedFile>,
|
uploaded_files: &mut Vec<UploadedFile>,
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
session_queue: &AuthQueue,
|
session_queue: &AuthQueue,
|
||||||
|
moderation_queue: &AutomatedModerationQueue,
|
||||||
) -> Result<HttpResponse, CreateError> {
|
) -> Result<HttpResponse, CreateError> {
|
||||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||||
|
|
||||||
@@ -333,6 +337,8 @@ async fn version_create_inner(
|
|||||||
.clone()
|
.clone()
|
||||||
.ok_or_else(|| CreateError::InvalidInput("`data` field is required".to_string()))?;
|
.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(
|
upload_file(
|
||||||
&mut field,
|
&mut field,
|
||||||
file_host,
|
file_host,
|
||||||
@@ -349,6 +355,7 @@ async fn version_create_inner(
|
|||||||
version_data.primary_file.is_some(),
|
version_data.primary_file.is_some(),
|
||||||
version_data.primary_file.as_deref() == Some(name),
|
version_data.primary_file.as_deref() == Some(name),
|
||||||
version_data.file_types.get(name).copied().flatten(),
|
version_data.file_types.get(name).copied().flatten(),
|
||||||
|
existing_file_names,
|
||||||
transaction,
|
transaction,
|
||||||
redis,
|
redis,
|
||||||
)
|
)
|
||||||
@@ -498,6 +505,19 @@ async fn version_create_inner(
|
|||||||
|
|
||||||
models::Project::clear_cache(project_id, None, Some(true), redis).await?;
|
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))
|
Ok(HttpResponse::Ok().json(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,6 +726,7 @@ async fn upload_file_to_version_inner(
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
file_data.file_types.get(name).copied().flatten(),
|
file_data.file_types.get(name).copied().flatten(),
|
||||||
|
version.files.iter().map(|x| x.filename.clone()).collect(),
|
||||||
transaction,
|
transaction,
|
||||||
&redis,
|
&redis,
|
||||||
)
|
)
|
||||||
@@ -759,11 +780,18 @@ pub async fn upload_file(
|
|||||||
ignore_primary: bool,
|
ignore_primary: bool,
|
||||||
force_primary: bool,
|
force_primary: bool,
|
||||||
file_type: Option<FileType>,
|
file_type: Option<FileType>,
|
||||||
|
other_file_names: Vec<String>,
|
||||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
redis: &RedisPool,
|
redis: &RedisPool,
|
||||||
) -> Result<(), CreateError> {
|
) -> Result<(), CreateError> {
|
||||||
let (file_name, file_extension) = get_name_ext(content_disposition)?;
|
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('/') {
|
if file_name.contains('/') {
|
||||||
return Err(CreateError::InvalidInput(
|
return Err(CreateError::InvalidInput(
|
||||||
"File names must not contain slashes!".to_string(),
|
"File names must not contain slashes!".to_string(),
|
||||||
|
|||||||
@@ -198,7 +198,6 @@ pub struct EditVersion {
|
|||||||
pub dependencies: Option<Vec<Dependency>>,
|
pub dependencies: Option<Vec<Dependency>>,
|
||||||
pub loaders: Option<Vec<Loader>>,
|
pub loaders: Option<Vec<Loader>>,
|
||||||
pub featured: Option<bool>,
|
pub featured: Option<bool>,
|
||||||
pub primary_file: Option<(String, String)>,
|
|
||||||
pub downloads: Option<u32>,
|
pub downloads: Option<u32>,
|
||||||
pub status: Option<VersionStatus>,
|
pub status: Option<VersionStatus>,
|
||||||
pub file_types: Option<Vec<EditVersionFileType>>,
|
pub file_types: Option<Vec<EditVersionFileType>>,
|
||||||
@@ -490,48 +489,6 @@ pub async fn version_edit_helper(
|
|||||||
.await?;
|
.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 {
|
if let Some(body) = &new_version.changelog {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -88,7 +88,11 @@ impl super::Validator for ModpackValidator {
|
|||||||
(x.ends_with("jar") || x.ends_with("zip"))
|
(x.ends_with("jar") || x.ends_with("zip"))
|
||||||
&& (x.starts_with("overrides/mods")
|
&& (x.starts_with("overrides/mods")
|
||||||
|| x.starts_with("client-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()))
|
.flat_map(|x| x.rsplit('/').next().map(|x| x.to_string()))
|
||||||
.collect::<Vec<String>>(),
|
.collect::<Vec<String>>(),
|
||||||
|
|||||||
Reference in New Issue
Block a user