diff --git a/sqlx-data.json b/sqlx-data.json index f56aeea53..a2cc8e443 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -5971,6 +5971,26 @@ }, "query": "\n UPDATE versions\n SET featured = $1\n WHERE (id = $2)\n " }, + "e60ea75112db37d3e73812e21b1907716e4762e06aa883af878e3be82e3f87d3": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Int8" + ] + } + }, + "query": "\n SELECT c.id FROM collections c\n WHERE c.user_id = $1\n " + }, "e6f5a150cbd3bd6b9bde9e5cdad224a45c96d678b69ec12508e81246710e3f6d": { "describe": { "columns": [ diff --git a/src/database/models/user_item.rs b/src/database/models/user_item.rs index f93a137f6..86ffb7afb 100644 --- a/src/database/models/user_item.rs +++ b/src/database/models/user_item.rs @@ -1,4 +1,5 @@ use super::ids::{ProjectId, UserId}; +use super::CollectionId; use crate::database::models::DatabaseError; use crate::models::ids::base62_impl::{parse_base62, to_base62}; use crate::models::users::{Badges, RecipientType, RecipientWallet}; @@ -320,6 +321,30 @@ impl User { Ok(projects) } + pub async fn get_collections<'a, E>( + user_id: UserId, + exec: E, + ) -> Result, sqlx::Error> + where + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, + { + use futures::stream::TryStreamExt; + + let projects = sqlx::query!( + " + SELECT c.id FROM collections c + WHERE c.user_id = $1 + ", + user_id as UserId, + ) + .fetch_many(exec) + .try_filter_map(|e| async { Ok(e.right().map(|m| CollectionId(m.id))) }) + .try_collect::>() + .await?; + + Ok(projects) + } + pub async fn get_backup_codes<'a, E>( user_id: UserId, exec: E, diff --git a/src/models/collections.rs b/src/models/collections.rs index dc2a62849..cd8cd90c3 100644 --- a/src/models/collections.rs +++ b/src/models/collections.rs @@ -68,6 +68,7 @@ impl From for Collection { pub enum CollectionStatus { Listed, Unlisted, + Private, Rejected, Unknown, } @@ -83,6 +84,7 @@ impl CollectionStatus { match string { "listed" => CollectionStatus::Listed, "unlisted" => CollectionStatus::Unlisted, + "private" => CollectionStatus::Private, "rejected" => CollectionStatus::Rejected, _ => CollectionStatus::Unknown, } @@ -91,6 +93,7 @@ impl CollectionStatus { match self { CollectionStatus::Listed => "listed", CollectionStatus::Unlisted => "unlisted", + CollectionStatus::Private => "private", CollectionStatus::Rejected => "rejected", CollectionStatus::Unknown => "unknown", } @@ -100,7 +103,7 @@ impl CollectionStatus { pub fn is_hidden(&self) -> bool { match self { CollectionStatus::Rejected => true, - + CollectionStatus::Private => true, CollectionStatus::Listed => false, CollectionStatus::Unlisted => false, CollectionStatus::Unknown => false, @@ -110,6 +113,7 @@ impl CollectionStatus { pub fn is_approved(&self) -> bool { match self { CollectionStatus::Listed => true, + CollectionStatus::Private => true, CollectionStatus::Unlisted => true, CollectionStatus::Rejected => false, CollectionStatus::Unknown => false, @@ -119,6 +123,7 @@ impl CollectionStatus { pub fn can_be_requested(&self) -> bool { match self { CollectionStatus::Listed => true, + CollectionStatus::Private => true, CollectionStatus::Unlisted => true, CollectionStatus::Rejected => false, CollectionStatus::Unknown => false, diff --git a/src/routes/maven.rs b/src/routes/maven.rs index f40a663fb..f8d0927e7 100644 --- a/src/routes/maven.rs +++ b/src/routes/maven.rs @@ -5,7 +5,10 @@ use crate::models::pats::Scopes; use crate::models::projects::{ProjectId, VersionId}; use crate::queue::session::AuthQueue; use crate::routes::ApiError; -use crate::{auth::{is_authorized, is_authorized_version, get_user_from_headers}, database}; +use crate::{ + auth::{get_user_from_headers, is_authorized, is_authorized_version}, + database, +}; use actix_web::{get, route, web, HttpRequest, HttpResponse}; use sqlx::PgPool; use std::collections::HashSet; diff --git a/src/routes/v2/mod.rs b/src/routes/v2/mod.rs index e2c9443fc..62595f807 100644 --- a/src/routes/v2/mod.rs +++ b/src/routes/v2/mod.rs @@ -1,7 +1,7 @@ mod admin; +mod analytics_get; mod collections; mod images; -mod analytics_get; mod moderation; mod notifications; pub(crate) mod project_creation; diff --git a/src/routes/v2/users.rs b/src/routes/v2/users.rs index ef590adb0..6adfe6a87 100644 --- a/src/routes/v2/users.rs +++ b/src/routes/v2/users.rs @@ -1,6 +1,7 @@ use crate::auth::{get_user_from_headers, AuthenticationError}; use crate::database::models::User; use crate::file_hosting::FileHost; +use crate::models::collections::{Collection, CollectionStatus}; use crate::models::notifications::Notification; use crate::models::pats::Scopes; use crate::models::projects::Project; @@ -30,6 +31,7 @@ pub fn config(cfg: &mut web::ServiceConfig) { web::scope("user") .service(user_get) .service(projects_list) + .service(collections_list) .service(user_delete) .service(user_edit) .service(user_icon_edit) @@ -157,6 +159,50 @@ pub async fn projects_list( } } +#[get("{user_id}/collections")] +pub async fn collections_list( + req: HttpRequest, + info: web::Path<(String,)>, + pool: web::Data, + redis: web::Data, + session_queue: web::Data, +) -> Result { + let user = get_user_from_headers( + &req, + &**pool, + &redis, + &session_queue, + Some(&[Scopes::COLLECTION_READ]), + ) + .await + .map(|x| x.1) + .ok(); + + let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?; + + if let Some(id) = id_option.map(|x| x.id) { + let user_id: UserId = id.into(); + + let can_view_private = user + .map(|y| y.role.is_mod() || y.id == user_id) + .unwrap_or(false); + + let project_data = User::get_collections(id, &**pool).await?; + + let response: Vec<_> = + crate::database::models::Collection::get_many(&project_data, &**pool, &redis) + .await? + .into_iter() + .filter(|x| can_view_private || matches!(x.status, CollectionStatus::Listed)) + .map(Collection::from) + .collect(); + + Ok(HttpResponse::Ok().json(response)) + } else { + Ok(HttpResponse::NotFound().body("")) + } +} + lazy_static! { static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap(); } diff --git a/src/validate/plugin.rs b/src/validate/plugin.rs index 8f783126f..353977f19 100644 --- a/src/validate/plugin.rs +++ b/src/validate/plugin.rs @@ -14,12 +14,7 @@ impl super::Validator for PluginYmlValidator { } fn get_supported_loaders(&self) -> &[&str] { - &[ - "bukkit", - "spigot", - "paper", - "purpur", - ] + &["bukkit", "spigot", "paper", "purpur"] } fn get_supported_game_versions(&self) -> SupportedGameVersions {