diff --git a/sqlx-data.json b/sqlx-data.json index 8b4202b76..6ef14c6ea 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -81,6 +81,26 @@ ] } }, + "1220d15a56dbf823eaa452fbafa17442ab0568bc81a31fa38e16e3df3278e5f9": { + "query": "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + null + ] + } + }, "1524c0462be70077736ac70fcd037fbf75651456b692e2ce40fa2e3fc8123984": { "query": "\n SELECT hashes.algorithm, hashes.hash FROM hashes\n WHERE hashes.file_id = $1\n ", "describe": { @@ -123,6 +143,74 @@ "nullable": [] } }, + "17e6d30c3693e9bd9f772f3dc4e2eafe75fdeecfdcf2746eac641f77ced6b8a8": { + "query": "\n SELECT u.id, u.github_id, u.name, u.email,\n u.avatar_url, u.username, u.bio,\n u.created, u.role FROM users u\n WHERE u.id IN (SELECT * FROM UNNEST($1::bigint[]))\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "github_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "avatar_url", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "username", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "bio", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "created", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "role", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int8Array" + ] + }, + "nullable": [ + false, + true, + true, + true, + true, + false, + true, + false, + false + ] + } + }, "1b74bdb59773ffd2a78a56e4d920bb83c322e180e6174c741d4bb722c353de43": { "query": "\n INSERT INTO loaders (loader)\n VALUES ($1)\n RETURNING id\n ", "describe": { @@ -203,6 +291,86 @@ ] } }, + "268efd8c8145bbb2bc70bd5ae0a5e6f90c5917ffd18575afcdeaf78b4f895d1e": { + "query": "\n SELECT id, title, description, downloads,\n icon_url, body_url, published,\n issues_url, source_url, wiki_url,\n team_id\n FROM mods\n WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "title", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "description", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "downloads", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "icon_url", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "body_url", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "published", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "issues_url", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "source_url", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "wiki_url", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "team_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8Array" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + false, + false, + true, + true, + true, + false + ] + } + }, "275939f581a82197b45b0d56248926063f09ef86754498a720c5568cdb1f5ae0": { "query": "SELECT user_id FROM team_members WHERE team_id=$1", "describe": { @@ -1183,6 +1351,74 @@ "nullable": [] } }, + "d98f0713c6bd3463f851644c88a2cf91601a499494ef1e2ad92a49d6798f5b2f": { + "query": "\n SELECT v.id, v.mod_id, v.author_id, v.name, v.version_number,\n v.changelog_url, v.date_published, v.downloads,\n v.release_channel\n FROM versions v\n WHERE v.id IN (SELECT * FROM UNNEST($1::bigint[]))\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "mod_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "author_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "version_number", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "changelog_url", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "date_published", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "downloads", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "release_channel", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int8Array" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false + ] + } + }, "deb81673526789bca38d39e64303f61d2a63febfdfb68136e58517af9f7792bc": { "query": "\n SELECT category FROM mods_categories\n INNER JOIN categories ON joining_category_id = id\n WHERE joining_mod_id = $1\n ", "describe": { @@ -1537,5 +1773,25 @@ null ] } + }, + "fdb2a6ea649bb23c69af5c756d6137e216603708ffccd4e9162fb1c9765a56aa": { + "query": "\n SELECT m.id FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id\n WHERE tm.user_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false + ] + } } } \ No newline at end of file diff --git a/src/database/models/mod_item.rs b/src/database/models/mod_item.rs index c5856a40f..0a91487e0 100644 --- a/src/database/models/mod_item.rs +++ b/src/database/models/mod_item.rs @@ -143,6 +143,46 @@ impl Mod { } } + pub async fn get_many<'a, E>(mod_ids: Vec, exec: E) -> Result, sqlx::Error> + where + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, + { + use futures::stream::TryStreamExt; + + let mod_ids_parsed: Vec = mod_ids.into_iter().map(|x| x.0).collect(); + let mods = sqlx::query!( + " + SELECT id, title, description, downloads, + icon_url, body_url, published, + issues_url, source_url, wiki_url, + team_id + FROM mods + WHERE id IN (SELECT * FROM UNNEST($1::bigint[])) + ", + &mod_ids_parsed + ) + .fetch_many(exec) + .try_filter_map(|e| async { + Ok(e.right().map(|m| Mod { + id: ModId(m.id), + team_id: TeamId(m.team_id), + title: m.title, + description: m.description, + downloads: m.downloads, + body_url: m.body_url, + icon_url: m.icon_url, + published: m.published, + issues_url: m.issues_url, + source_url: m.source_url, + wiki_url: m.wiki_url, + })) + }) + .try_collect::>() + .await?; + + Ok(mods) + } + pub async fn remove_full<'a, 'b, E>( id: ModId, exec: E, diff --git a/src/database/models/user_item.rs b/src/database/models/user_item.rs index b3aa7524b..caae46089 100644 --- a/src/database/models/user_item.rs +++ b/src/database/models/user_item.rs @@ -1,4 +1,4 @@ -use super::ids::UserId; +use super::ids::{ModId, UserId}; pub struct User { pub id: UserId, @@ -112,4 +112,62 @@ impl User { Ok(None) } } + + pub async fn get_many<'a, E>(user_ids: Vec, exec: E) -> Result, sqlx::Error> + where + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, + { + use futures::stream::TryStreamExt; + + let user_ids_parsed: Vec = user_ids.into_iter().map(|x| x.0).collect(); + let users = sqlx::query!( + " + SELECT u.id, u.github_id, u.name, u.email, + u.avatar_url, u.username, u.bio, + u.created, u.role FROM users u + WHERE u.id IN (SELECT * FROM UNNEST($1::bigint[])) + ", + &user_ids_parsed + ) + .fetch_many(exec) + .try_filter_map(|e| async { + Ok(e.right().map(|u| User { + id: UserId(u.id), + github_id: u.github_id, + name: u.name, + email: u.email, + avatar_url: u.avatar_url, + username: u.username, + bio: u.bio, + created: u.created, + role: u.role, + })) + }) + .try_collect::>() + .await?; + + Ok(users) + } + + pub async fn get_mods<'a, E>(user_id: UserId, exec: E) -> Result, sqlx::Error> + where + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, + { + use futures::stream::TryStreamExt; + + let mods = sqlx::query!( + " + SELECT m.id FROM mods m + INNER JOIN team_members tm ON tm.team_id = m.team_id + WHERE tm.user_id = $1 + ", + user_id as UserId, + ) + .fetch_many(exec) + .try_filter_map(|e| async { Ok(e.right().map(|m| ModId(m.id))) }) + .try_collect::>() + .await?; + + Ok(mods) + } } diff --git a/src/database/models/version_item.rs b/src/database/models/version_item.rs index 8cb191c89..2bbd5f81e 100644 --- a/src/database/models/version_item.rs +++ b/src/database/models/version_item.rs @@ -371,6 +371,46 @@ impl Version { } } + pub async fn get_many<'a, E>( + version_ids: Vec, + exec: E, + ) -> Result, sqlx::Error> + where + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, + { + use futures::stream::TryStreamExt; + + let version_ids_parsed: Vec = version_ids.into_iter().map(|x| x.0).collect(); + let versions = sqlx::query!( + " + SELECT v.id, v.mod_id, v.author_id, v.name, v.version_number, + v.changelog_url, v.date_published, v.downloads, + v.release_channel + FROM versions v + WHERE v.id IN (SELECT * FROM UNNEST($1::bigint[])) + ", + &version_ids_parsed + ) + .fetch_many(exec) + .try_filter_map(|e| async { + Ok(e.right().map(|v| Version { + id: VersionId(v.id), + mod_id: ModId(v.mod_id), + author_id: UserId(v.author_id), + name: v.name, + version_number: v.version_number, + changelog_url: v.changelog_url, + date_published: v.date_published, + downloads: v.downloads, + release_channel: ChannelId(v.release_channel), + })) + }) + .try_collect::>() + .await?; + + Ok(versions) + } + pub async fn get_full<'a, 'b, E>( id: VersionId, executor: E, diff --git a/src/main.rs b/src/main.rs index acd808355..4c1c1f854 100644 --- a/src/main.rs +++ b/src/main.rs @@ -204,6 +204,7 @@ async fn main() -> std::io::Result<()> { .configure(routes::auth_config) .configure(routes::tags_config) .configure(routes::mods_config) + .configure(routes::versions_config) .configure(routes::users_config), ) .default_service(web::get().to(routes::not_found)) diff --git a/src/models/ids.rs b/src/models/ids.rs index 1b98507bd..b59dfd303 100644 --- a/src/models/ids.rs +++ b/src/models/ids.rs @@ -13,6 +13,7 @@ pub use super::users::UserId; /// /// This method panics if `n` is 0 or greater than 11, since a `u64` /// can only represent up to 11 character base62 strings +#[allow(dead_code)] pub fn random_base62(n: usize) -> u64 { use rand::Rng; assert!(n > 0 && n <= 11); diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 20f1905cf..6aa8d6531 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -18,33 +18,36 @@ pub use self::not_found::not_found; pub fn mods_config(cfg: &mut web::ServiceConfig) { cfg.service(mods::mod_search); + cfg.service(mods::mods_get); cfg.service(mod_creation::mod_create); cfg.service( web::scope("mod") .service(mods::mod_get) .service(mods::mod_delete) - .service(web::scope("{mod_id}").configure(versions_config)), + .service(web::scope("{mod_id}").service(versions::version_list)), ); } pub fn versions_config(cfg: &mut web::ServiceConfig) { - cfg.service(versions::version_list) - .service(version_creation::version_create) - .service( - web::scope("version") - .service(versions::version_get) - .service(versions::version_delete) - .service(version_creation::upload_file_to_version), - ); + cfg.service(versions::versions_get); + cfg.service( + web::scope("version") + .service(versions::version_get) + .service(version_creation::version_create) + .service(versions::version_delete) + .service(version_creation::upload_file_to_version), + ); } pub fn users_config(cfg: &mut web::ServiceConfig) { cfg.service(users::user_auth_get); + cfg.service(users::users_get); cfg.service( web::scope("user") .service(users::user_get) + .service(users::mods_list) .service(users::user_delete), ); } @@ -53,6 +56,8 @@ pub fn users_config(cfg: &mut web::ServiceConfig) { pub enum ApiError { #[error("Internal server error")] DatabaseError(#[from] crate::database::models::DatabaseError), + #[error("Deserialization error: {0}")] + JsonError(#[from] serde_json::Error), #[error("Authentication Error")] AuthenticationError, } @@ -62,6 +67,7 @@ impl actix_web::ResponseError for ApiError { match self { ApiError::DatabaseError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, ApiError::AuthenticationError => actix_web::http::StatusCode::UNAUTHORIZED, + ApiError::JsonError(..) => actix_web::http::StatusCode::BAD_REQUEST, } } @@ -71,6 +77,7 @@ impl actix_web::ResponseError for ApiError { error: match self { ApiError::DatabaseError(..) => "database_error", ApiError::AuthenticationError => "unauthorized", + ApiError::JsonError(..) => "json_error", }, description: &self.to_string(), }, diff --git a/src/routes/mods.rs b/src/routes/mods.rs index f0b20b72a..026b8e11f 100644 --- a/src/routes/mods.rs +++ b/src/routes/mods.rs @@ -5,6 +5,7 @@ use crate::models; use crate::models::mods::SearchRequest; use crate::search::{search_for_mod, SearchError}; use actix_web::{delete, get, web, HttpRequest, HttpResponse}; +use serde::{Deserialize, Serialize}; use sqlx::PgPool; #[get("mod")] @@ -15,6 +16,49 @@ pub async fn mod_search( Ok(HttpResponse::Ok().json(results)) } +#[derive(Serialize, Deserialize)] +pub struct ModIds { + pub ids: String, +} + +// TODO: Make this return the full mod struct +#[get("mods")] +pub async fn mods_get( + web::Query(ids): web::Query, + pool: web::Data, +) -> Result { + let mod_ids = serde_json::from_str::>(&*ids.ids)? + .into_iter() + .map(|x| x.into()) + .collect(); + + let mods_data = database::models::Mod::get_many(mod_ids, &**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + let mods: Vec = mods_data + .into_iter() + .map(|m| models::mods::Mod { + id: m.id.into(), + team: m.team_id.into(), + title: m.title, + description: m.description, + body_url: m.body_url, + published: m.published, + + downloads: m.downloads as u32, + categories: vec![], + versions: vec![], + icon_url: m.icon_url, + issues_url: m.issues_url, + source_url: m.source_url, + wiki_url: m.wiki_url, + }) + .collect(); + + Ok(HttpResponse::Ok().json(mods)) +} + #[get("{id}")] pub async fn mod_get( info: web::Path<(models::ids::ModId,)>, @@ -48,7 +92,6 @@ pub async fn mod_get( Ok(HttpResponse::NotFound().body("")) } } - // TODO: The mod remains in meilisearch's index until the index is deleted #[delete("{id}")] pub async fn mod_delete( diff --git a/src/routes/users.rs b/src/routes/users.rs index 14b818745..458460c6e 100644 --- a/src/routes/users.rs +++ b/src/routes/users.rs @@ -1,7 +1,9 @@ use crate::auth::{check_is_moderator_from_headers, get_user_from_headers}; +use crate::database::models::User; use crate::models::users::{Role, UserId}; use crate::routes::ApiError; use actix_web::{delete, get, web, HttpRequest, HttpResponse}; +use serde::{Deserialize, Serialize}; use sqlx::PgPool; #[get("user")] @@ -22,13 +24,50 @@ pub async fn user_auth_get( )) } +#[derive(Serialize, Deserialize)] +pub struct UserIds { + pub ids: String, +} + +#[get("users")] +pub async fn users_get( + web::Query(ids): web::Query, + pool: web::Data, +) -> Result { + let user_ids = serde_json::from_str::>(&*ids.ids)? + .into_iter() + .map(|x| x.into()) + .collect(); + + let users_data = User::get_many(user_ids, &**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + let users: Vec = users_data + .into_iter() + .map(|data| crate::models::users::User { + id: data.id.into(), + github_id: data.github_id.map(|i| i as u64), + username: data.username, + name: data.name, + email: None, + avatar_url: data.avatar_url, + bio: data.bio, + created: data.created, + role: Role::from_string(&*data.role), + }) + .collect(); + + Ok(HttpResponse::Ok().json(users)) +} + #[get("{id}")] pub async fn user_get( info: web::Path<(UserId,)>, pool: web::Data, ) -> Result { let id = info.0; - let user_data = crate::database::models::User::get(id.into(), &**pool) + let user_data = User::get(id.into(), &**pool) .await .map_err(|e| ApiError::DatabaseError(e.into()))?; @@ -50,6 +89,38 @@ pub async fn user_get( } } +#[get("{user_id}/mods")] +pub async fn mods_list( + info: web::Path<(UserId,)>, + pool: web::Data, +) -> Result { + let id = info.0.into(); + + let user_exists = sqlx::query!( + "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)", + id as crate::database::models::UserId, + ) + .fetch_one(&**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))? + .exists; + + if user_exists.unwrap_or(false) { + let mod_data = User::get_mods(id, &**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + let response = mod_data + .into_iter() + .map(|v| v.into()) + .collect::>(); + + Ok(HttpResponse::Ok().json(response)) + } else { + Ok(HttpResponse::NotFound().body("")) + } +} + // TODO: Make this actually do stuff #[delete("{id}")] pub async fn user_delete( diff --git a/src/routes/version_creation.rs b/src/routes/version_creation.rs index 3474c2eff..3c398e7a2 100644 --- a/src/routes/version_creation.rs +++ b/src/routes/version_creation.rs @@ -15,6 +15,7 @@ use sqlx::postgres::PgPool; #[derive(Serialize, Deserialize, Clone)] pub struct InitialVersionData { + pub mod_id: ModId, pub file_parts: Vec, pub version_number: String, pub version_title: String, @@ -34,7 +35,6 @@ struct InitialFileData { #[post("version")] pub async fn version_create( req: HttpRequest, - url_data: actix_web::web::Path<(ModId,)>, payload: Multipart, client: Data, file_host: Data>, @@ -42,15 +42,12 @@ pub async fn version_create( let mut transaction = client.begin().await?; let mut uploaded_files = Vec::new(); - let mod_id = url_data.into_inner().0.into(); - let result = version_create_inner( req, payload, &mut transaction, &***file_host, &mut uploaded_files, - mod_id, ) .await; @@ -77,7 +74,6 @@ async fn version_create_inner( transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, file_host: &dyn FileHost, uploaded_files: &mut Vec, - mod_id: models::ModId, ) -> Result { let cdn_url = dotenv::var("CDN_URL")?; @@ -104,6 +100,7 @@ async fn version_create_inner( let version_create_data: InitialVersionData = serde_json::from_slice(&data)?; initial_version_data = Some(version_create_data); let version_create_data = initial_version_data.as_ref().unwrap(); + let mod_id: models::ModId = version_create_data.mod_id.into(); let results = sqlx::query!( "SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)", @@ -154,8 +151,7 @@ async fn version_create_inner( let version_id: VersionId = models::generate_version_id(transaction).await?.into(); let body_url = format!( "data/{}/changelogs/{}/body.md", - ModId::from(mod_id), - version_id + version_create_data.mod_id, version_id ); let uploaded_text = file_host @@ -180,7 +176,7 @@ async fn version_create_inner( version_builder = Some(VersionBuilder { version_id: version_id.into(), - mod_id, + mod_id: version_create_data.mod_id.into(), author_id: user.id.into(), name: version_create_data.version_title.clone(), version_number: version_create_data.version_number.clone(), @@ -210,7 +206,7 @@ async fn version_create_inner( uploaded_files, &cdn_url, &content_disposition, - ModId::from(mod_id), + version.mod_id.into(), &version.version_number, ) .await?; @@ -272,7 +268,7 @@ async fn version_create_inner( #[post("{version_id}/file")] pub async fn upload_file_to_version( req: HttpRequest, - url_data: actix_web::web::Path<(ModId, VersionId)>, + url_data: actix_web::web::Path<(VersionId,)>, payload: Multipart, client: Data, file_host: Data>, @@ -280,9 +276,7 @@ pub async fn upload_file_to_version( let mut transaction = client.begin().await?; let mut uploaded_files = Vec::new(); - let data = url_data.into_inner(); - let mod_id = models::ModId::from(data.0); - let version_id = models::VersionId::from(data.1); + let version_id = models::VersionId::from(url_data.0); let result = upload_file_to_version_inner( req, @@ -291,7 +285,6 @@ pub async fn upload_file_to_version( &***file_host, &mut uploaded_files, version_id, - mod_id, ) .await; @@ -319,7 +312,6 @@ async fn upload_file_to_version_inner( file_host: &dyn FileHost, uploaded_files: &mut Vec, version_id: models::VersionId, - mod_id: models::ModId, ) -> Result { let cdn_url = dotenv::var("CDN_URL")?; @@ -347,11 +339,6 @@ async fn upload_file_to_version_inner( )); } }; - if version.mod_id as u64 != mod_id.0 as u64 { - return Err(CreateError::InvalidInput( - "An invalid version id was supplied".to_string(), - )); - } if version.author_id as u64 != user.id.0 { return Err(CreateError::InvalidInput("Unauthorized".to_string())); diff --git a/src/routes/versions.rs b/src/routes/versions.rs index f76b8aa95..56d0efafa 100644 --- a/src/routes/versions.rs +++ b/src/routes/versions.rs @@ -3,6 +3,7 @@ use crate::auth::check_is_moderator_from_headers; use crate::database; use crate::models; use actix_web::{delete, get, web, HttpRequest, HttpResponse}; +use serde::{Deserialize, Serialize}; use sqlx::PgPool; // TODO: this needs filtering, and a better response type @@ -42,12 +43,56 @@ pub async fn version_list( } } -#[get("{version_id}")] -pub async fn version_get( - info: web::Path<(models::ids::ModId, models::ids::VersionId)>, +#[derive(Serialize, Deserialize)] +pub struct VersionIds { + pub ids: String, +} + +// TODO: Make this return the versions mod struct +#[get("versions")] +pub async fn versions_get( + web::Query(ids): web::Query, pool: web::Data, ) -> Result { - let id = info.1; + let version_ids = serde_json::from_str::>(&*ids.ids)? + .into_iter() + .map(|x| x.into()) + .collect(); + let versions_data = database::models::Version::get_many(version_ids, &**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + use models::mods::VersionType; + let versions: Vec = versions_data + .into_iter() + .map(|data| models::mods::Version { + id: data.id.into(), + mod_id: data.mod_id.into(), + author_id: data.author_id.into(), + + name: data.name, + version_number: data.version_number, + changelog_url: data.changelog_url, + date_published: data.date_published, + downloads: data.downloads as u32, + version_type: VersionType::Release, + + files: vec![], + dependencies: Vec::new(), // TODO: dependencies + game_versions: vec![], + loaders: vec![], + }) + .collect(); + + Ok(HttpResponse::Ok().json(versions)) +} + +#[get("{version_id}")] +pub async fn version_get( + info: web::Path<(models::ids::VersionId,)>, + pool: web::Data, +) -> Result { + let id = info.0; let version_data = database::models::Version::get_full(id.into(), &**pool) .await .map_err(|e| ApiError::DatabaseError(e.into()))?; @@ -55,11 +100,6 @@ pub async fn version_get( if let Some(data) = version_data { use models::mods::VersionType; - if models::ids::ModId::from(data.mod_id) != info.0 { - // Version doesn't belong to that mod - return Ok(HttpResponse::NotFound().body("")); - } - let response = models::mods::Version { id: data.id.into(), mod_id: data.mod_id.into(), diff --git a/src/search/indexing/mod.rs b/src/search/indexing/mod.rs index b3dfa58dd..c8930a0f7 100644 --- a/src/search/indexing/mod.rs +++ b/src/search/indexing/mod.rs @@ -41,6 +41,7 @@ pub struct IndexingSettings { } impl IndexingSettings { + #[allow(dead_code)] pub fn from_env() -> Self { let index_local = true; let index_external = dotenv::var("INDEX_CURSEFORGE")