diff --git a/src/database/models/project_item.rs b/src/database/models/project_item.rs index c2920dc84..e30d345f2 100644 --- a/src/database/models/project_item.rs +++ b/src/database/models/project_item.rs @@ -561,25 +561,24 @@ impl Project { } pub async fn get_full_from_slug_or_project_id<'a, 'b, E>( - slug_or_project_id: String, + slug_or_project_id: &str, executor: E, ) -> Result, sqlx::error::Error> where E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - let id_option = - crate::models::ids::base62_impl::parse_base62(&*slug_or_project_id.clone()).ok(); + let id_option = crate::models::ids::base62_impl::parse_base62(slug_or_project_id).ok(); if let Some(id) = id_option { let mut project = Project::get_full(ProjectId(id as i64), executor).await?; if project.is_none() { - project = Project::get_full_from_slug(&slug_or_project_id, executor).await?; + project = Project::get_full_from_slug(slug_or_project_id, executor).await?; } Ok(project) } else { - let project = Project::get_full_from_slug(&slug_or_project_id, executor).await?; + let project = Project::get_full_from_slug(slug_or_project_id, executor).await?; Ok(project) } } diff --git a/src/routes/maven.rs b/src/routes/maven.rs index e1605365c..ccc5aac3f 100644 --- a/src/routes/maven.rs +++ b/src/routes/maven.rs @@ -1,7 +1,8 @@ -use crate::database; +use crate::database::models::version_item::{QueryFile, QueryVersion}; use crate::models::projects::ProjectId; use crate::routes::ApiError; use crate::util::auth::get_user_from_headers; +use crate::{database, util::auth::is_authorized}; use actix_web::{get, web, HttpRequest, HttpResponse}; use sqlx::PgPool; use yaserde_derive::YaSerialize; @@ -51,15 +52,11 @@ pub struct MavenPom { #[get("maven/modrinth/{id}/maven-metadata.xml")] pub async fn maven_metadata( req: HttpRequest, - info: web::Path<(String,)>, + web::Path((project_id,)): web::Path<(String,)>, pool: web::Data, ) -> Result { - let string = info.into_inner().0; - let project_data = - database::models::Project::get_full_from_slug_or_project_id(string, &**pool).await?; - - let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); + database::models::Project::get_full_from_slug_or_project_id(&project_id, &**pool).await?; let data = if let Some(data) = project_data { data @@ -67,32 +64,12 @@ pub async fn maven_metadata( return Ok(HttpResponse::NotFound().body("")); }; - let mut authorized = !data.status.is_hidden(); + let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); - if let Some(user) = user_option { - if !authorized { - if user.role.is_mod() { - authorized = true; - } else { - let user_id: database::models::ids::UserId = user.id.into(); - - let project_exists = sqlx::query!( - "SELECT EXISTS(SELECT 1 FROM team_members WHERE team_id = $1 AND user_id = $2)", - data.inner.team_id as database::models::ids::TeamId, - user_id as database::models::ids::UserId, - ) - .fetch_one(&**pool) - .await? - .exists; - - authorized = project_exists.unwrap_or(false); - } - } - } - - if !authorized { + if !is_authorized(&data, &user_option, &pool).await? { return Ok(HttpResponse::NotFound().body("")); } + let version_names = sqlx::query!( " SELECT version_number, version_type @@ -137,22 +114,11 @@ pub async fn maven_metadata( #[get("maven/modrinth/{id}/{versionnum}/{file}")] pub async fn version_file( req: HttpRequest, - web::Path((string, vnum, file)): web::Path<(String, String, String)>, + web::Path((project_id, vnum, file)): web::Path<(String, String, String)>, pool: web::Data, ) -> Result { - let id_option: Option = serde_json::from_str(&*format!("\"{}\"", string)).ok(); - - let project_data = if let Some(id) = id_option { - match database::models::Project::get_full(id.into(), &**pool).await { - Ok(Some(data)) => Ok(Some(data)), - Ok(None) => database::models::Project::get_full_from_slug(&string, &**pool).await, - Err(e) => Err(e), - } - } else { - database::models::Project::get_full_from_slug(&string, &**pool).await - }?; - - let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); + let project_data = + database::models::Project::get_full_from_slug_or_project_id(&project_id, &**pool).await?; let data = if let Some(data) = project_data { data @@ -160,30 +126,9 @@ pub async fn version_file( return Ok(HttpResponse::NotFound().body("")); }; - let mut authorized = !data.status.is_hidden(); + let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); - if let Some(user) = user_option { - if !authorized { - if user.role.is_mod() { - authorized = true; - } else { - let user_id: database::models::ids::UserId = user.id.into(); - - let project_exists = sqlx::query!( - "SELECT EXISTS(SELECT 1 FROM team_members WHERE team_id = $1 AND user_id = $2)", - data.inner.team_id as database::models::ids::TeamId, - user_id as database::models::ids::UserId, - ) - .fetch_one(&**pool) - .await? - .exists; - - authorized = project_exists.unwrap_or(false); - } - } - } - - if !authorized { + if !is_authorized(&data, &user_option, &pool).await? { return Ok(HttpResponse::NotFound().body("")); } @@ -209,7 +154,7 @@ pub async fn version_file( return Ok(HttpResponse::NotFound().body("")); }; - if file == format!("{}-{}.pom", &string, &version.version_number) { + if file == format!("{}-{}.pom", &project_id, &version.version_number) { let respdata = MavenPom { schema_location: "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" @@ -217,7 +162,7 @@ pub async fn version_file( xsi: "http://www.w3.org/2001/XMLSchema-instance".to_string(), model_version: "4.0.0".to_string(), group_id: "maven.modrinth".to_string(), - artifact_id: string, + artifact_id: project_id, version: version.version_number, name: data.inner.title, description: data.inner.description, @@ -229,7 +174,7 @@ pub async fn version_file( return Ok(HttpResponse::TemporaryRedirect() .header("Location", &*selected_file.url) .body("")); - } else if file == format!("{}-{}.jar", &string, &version.version_number) { + } else if file == format!("{}-{}.jar", &project_id, &version.version_number) { if let Some(selected_file) = version.files.iter().find(|x| x.primary) { return Ok(HttpResponse::TemporaryRedirect() .header("Location", &*selected_file.url) @@ -243,3 +188,145 @@ pub async fn version_file( Ok(HttpResponse::NotFound().body("")) } + +#[get("maven/modrinth/{id}/{versionnum}/{file}.sha1")] +pub async fn version_file_sha1( + req: HttpRequest, + web::Path((project_id, vnum, file)): web::Path<(String, String, String)>, + pool: web::Data, +) -> Result { + let project_data = + database::models::Project::get_full_from_slug_or_project_id(&project_id, &**pool).await?; + + let data = if let Some(data) = project_data { + data + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); + + if !is_authorized(&data, &user_option, &pool).await? { + return Ok(HttpResponse::NotFound().body("")); + } + + let vid = if let Some(vid) = sqlx::query!( + "SELECT id FROM versions WHERE mod_id = $1 AND version_number = $2", + data.inner.id as database::models::ids::ProjectId, + vnum + ) + .fetch_optional(&**pool) + .await? + { + vid + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let version = if let Some(version) = + database::models::Version::get_full(database::models::ids::VersionId(vid.id), &**pool) + .await? + { + version + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let selected_file = + if let Some(selected_file) = version.files.iter().find(|x| x.filename == file) { + selected_file + } else if file == format!("{}-{}.jar", &project_id, &version.version_number) { + if let Some(selected_file) = version.files.iter().last() { + selected_file + } else { + return Ok(HttpResponse::NotFound().body("")); + } + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let hash_bytes = if let Some(val) = selected_file.hashes.get("sha1") { + val + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let hash_text = if let Some(text) = std::str::from_utf8(hash_bytes).ok() { + text.to_string() + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + return Ok(HttpResponse::Ok().body(hash_text)); +} + +#[get("maven/modrinth/{id}/{versionnum}/{file}.sha512")] +pub async fn version_file_sha512( + req: HttpRequest, + web::Path((project_id, vnum, file)): web::Path<(String, String, String)>, + pool: web::Data, +) -> Result { + let project_data = + database::models::Project::get_full_from_slug_or_project_id(&project_id, &**pool).await?; + + let data = if let Some(data) = project_data { + data + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); + + if !is_authorized(&data, &user_option, &pool).await? { + return Ok(HttpResponse::NotFound().body("")); + } + + let vid = if let Some(vid) = sqlx::query!( + "SELECT id FROM versions WHERE mod_id = $1 AND version_number = $2", + data.inner.id as database::models::ids::ProjectId, + vnum + ) + .fetch_optional(&**pool) + .await? + { + vid + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let version = if let Some(version) = + database::models::Version::get_full(database::models::ids::VersionId(vid.id), &**pool) + .await? + { + version + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let selected_file = + if let Some(selected_file) = version.files.iter().find(|x| x.filename == file) { + selected_file + } else if file == format!("{}-{}.jar", &project_id, &version.version_number) { + if let Some(selected_file) = version.files.iter().last() { + selected_file + } else { + return Ok(HttpResponse::NotFound().body("")); + } + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let hash_bytes = if let Some(val) = selected_file.hashes.get("sha512") { + val + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + let hash_text = if let Some(text) = std::str::from_utf8(hash_bytes).ok() { + text.to_string() + } else { + return Ok(HttpResponse::NotFound().body("")); + }; + + return Ok(HttpResponse::Ok().body(hash_text)); +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 8a198355d..e40644733 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -70,6 +70,8 @@ pub fn projects_config(cfg: &mut web::ServiceConfig) { pub fn maven_config(cfg: &mut web::ServiceConfig) { cfg.service(maven::maven_metadata); + cfg.service(maven::version_file_sha512); + cfg.service(maven::version_file_sha1); cfg.service(maven::version_file); } diff --git a/src/routes/projects.rs b/src/routes/projects.rs index 245a51d9e..b99dc2d14 100644 --- a/src/routes/projects.rs +++ b/src/routes/projects.rs @@ -71,8 +71,7 @@ pub async fn project_get( let string = info.into_inner().0; let project_data = - database::models::Project::get_full_from_slug_or_project_id(string.clone(), &**pool) - .await?; + database::models::Project::get_full_from_slug_or_project_id(&string, &**pool).await?; let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); @@ -251,8 +250,7 @@ pub async fn project_edit( let string = info.into_inner().0; let result = - database::models::Project::get_full_from_slug_or_project_id(string.clone(), &**pool) - .await?; + database::models::Project::get_full_from_slug_or_project_id(&string, &**pool).await?; if let Some(project_item) = result { let id = project_item.inner.id;