From a6f7e76bec2da12b52515b085ea3667d3de3b8bf Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 18 Dec 2024 11:39:19 +0100 Subject: [PATCH] community icon/banner --- crates/api_common/src/image.rs | 8 +++ crates/api_crud/src/community/create.rs | 10 ---- crates/routes/src/images/upload.rs | 74 +++++++++++++++++++++---- src/api_routes_v4.rs | 10 +++- 4 files changed, 80 insertions(+), 22 deletions(-) diff --git a/crates/api_common/src/image.rs b/crates/api_common/src/image.rs index c28832921..e93b367f6 100644 --- a/crates/api_common/src/image.rs +++ b/crates/api_common/src/image.rs @@ -1,3 +1,4 @@ +use lemmy_db_schema::newtypes::CommunityId; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; #[cfg(feature = "full")] @@ -44,3 +45,10 @@ pub struct UploadImageResponse { pub filename: String, pub delete_token: String, } + +/// Parameter for setting community icon or banner. Can't use POST data here as it already contains +/// the image data. +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] +pub struct CommunityIdQuery { + pub id: CommunityId, +} diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index c81157950..0437396cc 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -13,7 +13,6 @@ use lemmy_api_common::{ is_admin, local_site_to_slur_regex, process_markdown_opt, - proxy_image_link_api, EndpointType, }, }; @@ -31,7 +30,6 @@ use lemmy_db_schema::{ }, }, traits::{ApubActor, Crud, Followable, Joinable}, - utils::diesel_url_create, }; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::{ @@ -76,12 +74,6 @@ pub async fn create_community( check_slurs(desc, &slur_regex)?; } - let icon = diesel_url_create(data.icon.as_deref())?; - let icon = proxy_image_link_api(icon, &context).await?; - - let banner = diesel_url_create(data.banner.as_deref())?; - let banner = proxy_image_link_api(banner, &context).await?; - is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?; if let Some(desc) = &data.description { @@ -108,8 +100,6 @@ pub async fn create_community( let community_form = CommunityInsertForm { sidebar, description, - icon, - banner, nsfw: data.nsfw, actor_id: Some(community_actor_id.clone()), private_key: Some(keypair.private_key), diff --git a/crates/routes/src/images/upload.rs b/crates/routes/src/images/upload.rs index 6b24940b9..877421641 100644 --- a/crates/routes/src/images/upload.rs +++ b/crates/routes/src/images/upload.rs @@ -2,13 +2,15 @@ use super::utils::{adapt_request, delete_old_image, make_send}; use actix_web::{self, web::*, HttpRequest}; use lemmy_api_common::{ context::LemmyContext, - image::UploadImageResponse, + image::{CommunityIdQuery, UploadImageResponse}, request::PictrsResponse, + utils::is_mod_or_admin, LemmyErrorType, SuccessResponse, }; use lemmy_db_schema::{ source::{ + community::{Community, CommunityUpdateForm}, images::{LocalImage, LocalImageForm}, person::{Person, PersonUpdateForm}, }, @@ -50,11 +52,11 @@ pub async fn upload_user_avatar( let image = do_upload_image(req, body, Avatar, &local_user_view, &context).await?; delete_old_image(&local_user_view.person.avatar, &context).await?; - let person_form = PersonUpdateForm { + let form = PersonUpdateForm { avatar: Some(Some(image.image_url.into())), ..Default::default() }; - Person::update(&mut context.pool(), local_user_view.person.id, &person_form).await?; + Person::update(&mut context.pool(), local_user_view.person.id, &form).await?; Ok(Json(SuccessResponse::default())) } @@ -68,11 +70,55 @@ pub async fn upload_user_banner( let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?; delete_old_image(&local_user_view.person.banner, &context).await?; - let person_form = PersonUpdateForm { + let form = PersonUpdateForm { banner: Some(Some(image.image_url.into())), ..Default::default() }; - Person::update(&mut context.pool(), local_user_view.person.id, &person_form).await?; + Person::update(&mut context.pool(), local_user_view.person.id, &form).await?; + + Ok(Json(SuccessResponse::default())) +} + +pub async fn upload_community_icon( + req: HttpRequest, + query: Query, + body: Payload, + local_user_view: LocalUserView, + context: Data, +) -> LemmyResult> { + let community: Community = Community::read(&mut context.pool(), query.id).await?; + is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?; + + let image = do_upload_image(req, body, Avatar, &local_user_view, &context).await?; + delete_old_image(&community.icon, &context).await?; + + let form = CommunityUpdateForm { + icon: Some(Some(image.image_url.into())), + ..Default::default() + }; + Community::update(&mut context.pool(), community.id, &form).await?; + + Ok(Json(SuccessResponse::default())) +} + +pub async fn upload_community_banner( + req: HttpRequest, + query: Query, + body: Payload, + local_user_view: LocalUserView, + context: Data, +) -> LemmyResult> { + let community: Community = Community::read(&mut context.pool(), query.id).await?; + is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.id).await?; + + let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?; + delete_old_image(&community.icon, &context).await?; + + let form = CommunityUpdateForm { + banner: Some(Some(image.image_url.into())), + ..Default::default() + }; + Community::update(&mut context.pool(), community.id, &form).await?; Ok(Json(SuccessResponse::default())) } @@ -84,29 +130,35 @@ pub async fn do_upload_image( local_user_view: &LocalUserView, context: &Data, ) -> LemmyResult { - let pictrs_config = context.settings().pictrs()?; - let image_url = format!("{}image", pictrs_config.url); + let pictrs = context.settings().pictrs()?; + let image_url = format!("{}image", pictrs.url); let mut client_req = adapt_request(&req, image_url); client_req = match upload_type { Avatar => { - let max_size = context.settings().pictrs()?.max_avatar_size.to_string(); + let max_size = pictrs.max_avatar_size.to_string(); + client_req.query(&[ + ("resize", max_size.as_ref()), + ("allow_animation", "false"), + ("allow_video", "false"), + ]) + } + Banner => { + let max_size = pictrs.max_banner_size.to_string(); client_req.query(&[ ("resize", max_size.as_ref()), ("allow_animation", "false"), ("allow_video", "false"), ]) } - // TODO: same as above but using `max_banner_size` - // Banner => {} _ => client_req, }; if let Some(addr) = req.head().peer_addr { client_req = client_req.header("X-Forwarded-For", addr.to_string()) }; let res = client_req - .timeout(Duration::from_secs(pictrs_config.upload_timeout)) + .timeout(Duration::from_secs(pictrs.upload_timeout)) .body(Body::wrap_stream(make_send(body))) .send() .await? diff --git a/src/api_routes_v4.rs b/src/api_routes_v4.rs index f725b66ca..c7f0dd354 100644 --- a/src/api_routes_v4.rs +++ b/src/api_routes_v4.rs @@ -162,7 +162,13 @@ use lemmy_routes::images::{ delete_image, download::{get_image, image_proxy}, pictrs_health, - upload::{upload_image, upload_user_avatar, upload_user_banner}, + upload::{ + upload_community_banner, + upload_community_icon, + upload_image, + upload_user_avatar, + upload_user_banner, + }, }; use lemmy_utils::rate_limit::RateLimitCell; @@ -205,6 +211,8 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .route("/transfer", post().to(transfer_community)) .route("/ban_user", post().to(ban_from_community)) .route("/mod", post().to(add_mod_to_community)) + .route("/icon", post().to(upload_community_icon)) + .route("/banner", post().to(upload_community_banner)) .service( scope("/pending_follows") .route("/count", get().to(get_pending_follows_count))