mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-23 03:11:32 +00:00
site icon/banner
This commit is contained in:
parent
a6f7e76bec
commit
ada8f1ba8e
10 changed files with 58 additions and 112 deletions
|
@ -11,7 +11,7 @@ killall -s1 lemmy_server || true
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pnpm i
|
pnpm i
|
||||||
pnpm api-test-image || true
|
pnpm api-test || true
|
||||||
|
|
||||||
killall -s1 lemmy_server || true
|
killall -s1 lemmy_server || true
|
||||||
killall -s1 pict-rs || true
|
killall -s1 pict-rs || true
|
||||||
|
|
|
@ -177,12 +177,6 @@ pub struct EditCommunity {
|
||||||
/// A shorter, one line description of your community.
|
/// A shorter, one line description of your community.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// An icon URL.
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub icon: Option<String>,
|
|
||||||
/// A banner URL.
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub banner: Option<String>,
|
|
||||||
/// Whether its an NSFW community.
|
/// Whether its an NSFW community.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub nsfw: Option<bool>,
|
pub nsfw: Option<bool>,
|
||||||
|
|
|
@ -9,13 +9,10 @@ use activitypub_federation::config::Data;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use encoding_rs::{Encoding, UTF_8};
|
use encoding_rs::{Encoding, UTF_8};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::source::{
|
||||||
newtypes::DbUrl,
|
images::{ImageDetailsForm, LocalImage, LocalImageForm},
|
||||||
source::{
|
post::{Post, PostUpdateForm},
|
||||||
images::{ImageDetailsForm, LocalImage, LocalImageForm},
|
site::Site,
|
||||||
post::{Post, PostUpdateForm},
|
|
||||||
site::Site,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
|
@ -472,30 +469,6 @@ async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Lemm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When adding a new avatar, banner or similar image, delete the old one.
|
|
||||||
/// TODO: remove this function
|
|
||||||
pub async fn replace_image(
|
|
||||||
new_image: &Option<Option<DbUrl>>,
|
|
||||||
old_image: &Option<DbUrl>,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
if let (Some(Some(new_image)), Some(old_image)) = (new_image, old_image) {
|
|
||||||
// Note: Oftentimes front ends will include the current image in the form.
|
|
||||||
// In this case, deleting `old_image` would also be deletion of `new_image`,
|
|
||||||
// so the deletion must be skipped for the image to be kept.
|
|
||||||
if new_image != old_image {
|
|
||||||
// Ignore errors because image may be stored externally.
|
|
||||||
let image = LocalImage::delete_by_url(&mut context.pool(), old_image)
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
if let Some(image) = image {
|
|
||||||
delete_image_from_pictrs(&image.pictrs_alias, &image.pictrs_delete_token, context).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
|
|
@ -201,10 +201,6 @@ pub struct CreateSite {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub icon: Option<String>,
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub banner: Option<String>,
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub enable_nsfw: Option<bool>,
|
pub enable_nsfw: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub community_creation_admin_only: Option<bool>,
|
pub community_creation_admin_only: Option<bool>,
|
||||||
|
@ -298,12 +294,6 @@ pub struct EditSite {
|
||||||
/// A shorter, one line description of your site.
|
/// A shorter, one line description of your site.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// A url for your site's icon.
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub icon: Option<String>,
|
|
||||||
/// A url for your site's banner.
|
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
|
||||||
pub banner: Option<String>,
|
|
||||||
/// Whether to enable NSFW.
|
/// Whether to enable NSFW.
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub enable_nsfw: Option<bool>,
|
pub enable_nsfw: Option<bool>,
|
||||||
|
|
|
@ -1131,31 +1131,6 @@ pub async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult<
|
||||||
proxy_image_link_internal(link, context.settings().pictrs()?.image_mode(), context).await
|
proxy_image_link_internal(link, context.settings().pictrs()?.image_mode(), context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn proxy_image_link_opt_api(
|
|
||||||
link: Option<Option<DbUrl>>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> LemmyResult<Option<Option<DbUrl>>> {
|
|
||||||
if let Some(Some(link)) = link {
|
|
||||||
proxy_image_link(link.into(), context)
|
|
||||||
.await
|
|
||||||
.map(Some)
|
|
||||||
.map(Some)
|
|
||||||
} else {
|
|
||||||
Ok(link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn proxy_image_link_api(
|
|
||||||
link: Option<DbUrl>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> LemmyResult<Option<DbUrl>> {
|
|
||||||
if let Some(link) = link {
|
|
||||||
proxy_image_link(link.into(), context).await.map(Some)
|
|
||||||
} else {
|
|
||||||
Ok(link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn proxy_image_link_opt_apub(
|
pub async fn proxy_image_link_opt_apub(
|
||||||
link: Option<Url>,
|
link: Option<Url>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
|
|
@ -6,14 +6,12 @@ use lemmy_api_common::{
|
||||||
build_response::build_community_response,
|
build_response::build_community_response,
|
||||||
community::{CommunityResponse, EditCommunity},
|
community::{CommunityResponse, EditCommunity},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
request::replace_image,
|
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_mod_action,
|
check_community_mod_action,
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
process_markdown_opt,
|
process_markdown_opt,
|
||||||
proxy_image_link_opt_api,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -23,7 +21,7 @@ use lemmy_db_schema::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{diesel_string_update, diesel_url_update},
|
utils::diesel_string_update,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -58,14 +56,6 @@ pub async fn update_community(
|
||||||
|
|
||||||
let old_community = Community::read(&mut context.pool(), data.community_id).await?;
|
let old_community = Community::read(&mut context.pool(), data.community_id).await?;
|
||||||
|
|
||||||
let icon = diesel_url_update(data.icon.as_deref())?;
|
|
||||||
replace_image(&icon, &old_community.icon, &context).await?;
|
|
||||||
let icon = proxy_image_link_opt_api(icon, &context).await?;
|
|
||||||
|
|
||||||
let banner = diesel_url_update(data.banner.as_deref())?;
|
|
||||||
replace_image(&banner, &old_community.banner, &context).await?;
|
|
||||||
let banner = proxy_image_link_opt_api(banner, &context).await?;
|
|
||||||
|
|
||||||
// Verify its a mod (only mods can edit it)
|
// Verify its a mod (only mods can edit it)
|
||||||
check_community_mod_action(
|
check_community_mod_action(
|
||||||
&local_user_view.person,
|
&local_user_view.person,
|
||||||
|
@ -91,8 +81,6 @@ pub async fn update_community(
|
||||||
title: data.title.clone(),
|
title: data.title.clone(),
|
||||||
sidebar,
|
sidebar,
|
||||||
description,
|
description,
|
||||||
icon,
|
|
||||||
banner,
|
|
||||||
nsfw: data.nsfw,
|
nsfw: data.nsfw,
|
||||||
posting_restricted_to_mods: data.posting_restricted_to_mods,
|
posting_restricted_to_mods: data.posting_restricted_to_mods,
|
||||||
visibility: data.visibility,
|
visibility: data.visibility,
|
||||||
|
|
|
@ -13,7 +13,6 @@ use lemmy_api_common::{
|
||||||
local_site_rate_limit_to_rate_limit_config,
|
local_site_rate_limit_to_rate_limit_config,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
process_markdown_opt,
|
process_markdown_opt,
|
||||||
proxy_image_link_api,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -24,7 +23,7 @@ use lemmy_db_schema::{
|
||||||
site::{Site, SiteUpdateForm},
|
site::{Site, SiteUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{diesel_string_update, diesel_url_create},
|
utils::diesel_string_update,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -63,18 +62,10 @@ pub async fn create_site(
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?;
|
let sidebar = process_markdown_opt(&data.sidebar, &slur_regex, &url_blocklist, &context).await?;
|
||||||
|
|
||||||
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?;
|
|
||||||
|
|
||||||
let site_form = SiteUpdateForm {
|
let site_form = SiteUpdateForm {
|
||||||
name: Some(data.name.clone()),
|
name: Some(data.name.clone()),
|
||||||
sidebar: diesel_string_update(sidebar.as_deref()),
|
sidebar: diesel_string_update(sidebar.as_deref()),
|
||||||
description: diesel_string_update(data.description.as_deref()),
|
description: diesel_string_update(data.description.as_deref()),
|
||||||
icon: Some(icon),
|
|
||||||
banner: Some(banner),
|
|
||||||
actor_id: Some(actor_id),
|
actor_id: Some(actor_id),
|
||||||
last_refreshed_at: Some(Utc::now()),
|
last_refreshed_at: Some(Utc::now()),
|
||||||
inbox_url,
|
inbox_url,
|
||||||
|
|
|
@ -5,7 +5,6 @@ use actix_web::web::Json;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
request::replace_image,
|
|
||||||
site::{EditSite, SiteResponse},
|
site::{EditSite, SiteResponse},
|
||||||
utils::{
|
utils::{
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
|
@ -13,7 +12,6 @@ use lemmy_api_common::{
|
||||||
local_site_rate_limit_to_rate_limit_config,
|
local_site_rate_limit_to_rate_limit_config,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
process_markdown_opt,
|
process_markdown_opt,
|
||||||
proxy_image_link_opt_api,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -26,7 +24,7 @@ use lemmy_db_schema::{
|
||||||
site::{Site, SiteUpdateForm},
|
site::{Site, SiteUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{diesel_string_update, diesel_url_update},
|
utils::diesel_string_update,
|
||||||
RegistrationMode,
|
RegistrationMode,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
|
@ -72,20 +70,10 @@ pub async fn update_site(
|
||||||
.as_deref(),
|
.as_deref(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let icon = diesel_url_update(data.icon.as_deref())?;
|
|
||||||
replace_image(&icon, &site.icon, &context).await?;
|
|
||||||
let icon = proxy_image_link_opt_api(icon, &context).await?;
|
|
||||||
|
|
||||||
let banner = diesel_url_update(data.banner.as_deref())?;
|
|
||||||
replace_image(&banner, &site.banner, &context).await?;
|
|
||||||
let banner = proxy_image_link_opt_api(banner, &context).await?;
|
|
||||||
|
|
||||||
let site_form = SiteUpdateForm {
|
let site_form = SiteUpdateForm {
|
||||||
name: data.name.clone(),
|
name: data.name.clone(),
|
||||||
sidebar,
|
sidebar,
|
||||||
description: diesel_string_update(data.description.as_deref()),
|
description: diesel_string_update(data.description.as_deref()),
|
||||||
icon,
|
|
||||||
banner,
|
|
||||||
content_warning: diesel_string_update(data.content_warning.as_deref()),
|
content_warning: diesel_string_update(data.content_warning.as_deref()),
|
||||||
updated: Some(Some(Utc::now())),
|
updated: Some(Some(Utc::now())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
image::{CommunityIdQuery, UploadImageResponse},
|
image::{CommunityIdQuery, UploadImageResponse},
|
||||||
request::PictrsResponse,
|
request::PictrsResponse,
|
||||||
utils::is_mod_or_admin,
|
utils::{is_admin, is_mod_or_admin},
|
||||||
LemmyErrorType,
|
LemmyErrorType,
|
||||||
SuccessResponse,
|
SuccessResponse,
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,7 @@ use lemmy_db_schema::{
|
||||||
community::{Community, CommunityUpdateForm},
|
community::{Community, CommunityUpdateForm},
|
||||||
images::{LocalImage, LocalImageForm},
|
images::{LocalImage, LocalImageForm},
|
||||||
person::{Person, PersonUpdateForm},
|
person::{Person, PersonUpdateForm},
|
||||||
|
site::{Site, SiteUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
|
@ -112,7 +113,7 @@ pub async fn upload_community_banner(
|
||||||
is_mod_or_admin(&mut context.pool(), &local_user_view.person, community.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?;
|
let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?;
|
||||||
delete_old_image(&community.icon, &context).await?;
|
delete_old_image(&community.banner, &context).await?;
|
||||||
|
|
||||||
let form = CommunityUpdateForm {
|
let form = CommunityUpdateForm {
|
||||||
banner: Some(Some(image.image_url.into())),
|
banner: Some(Some(image.image_url.into())),
|
||||||
|
@ -123,6 +124,48 @@ pub async fn upload_community_banner(
|
||||||
Ok(Json(SuccessResponse::default()))
|
Ok(Json(SuccessResponse::default()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn upload_site_icon(
|
||||||
|
req: HttpRequest,
|
||||||
|
body: Payload,
|
||||||
|
local_user_view: LocalUserView,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<Json<SuccessResponse>> {
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
let site = Site::read_local(&mut context.pool()).await?;
|
||||||
|
|
||||||
|
let image = do_upload_image(req, body, Avatar, &local_user_view, &context).await?;
|
||||||
|
delete_old_image(&site.icon, &context).await?;
|
||||||
|
|
||||||
|
let form = SiteUpdateForm {
|
||||||
|
icon: Some(Some(image.image_url.into())),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
Site::update(&mut context.pool(), site.id, &form).await?;
|
||||||
|
|
||||||
|
Ok(Json(SuccessResponse::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn upload_site_banner(
|
||||||
|
req: HttpRequest,
|
||||||
|
body: Payload,
|
||||||
|
local_user_view: LocalUserView,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<Json<SuccessResponse>> {
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
let site = Site::read_local(&mut context.pool()).await?;
|
||||||
|
|
||||||
|
let image = do_upload_image(req, body, Banner, &local_user_view, &context).await?;
|
||||||
|
delete_old_image(&site.banner, &context).await?;
|
||||||
|
|
||||||
|
let form = SiteUpdateForm {
|
||||||
|
banner: Some(Some(image.image_url.into())),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
Site::update(&mut context.pool(), site.id, &form).await?;
|
||||||
|
|
||||||
|
Ok(Json(SuccessResponse::default()))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn do_upload_image(
|
pub async fn do_upload_image(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
body: Payload,
|
body: Payload,
|
||||||
|
|
|
@ -166,6 +166,8 @@ use lemmy_routes::images::{
|
||||||
upload_community_banner,
|
upload_community_banner,
|
||||||
upload_community_icon,
|
upload_community_icon,
|
||||||
upload_image,
|
upload_image,
|
||||||
|
upload_site_banner,
|
||||||
|
upload_site_icon,
|
||||||
upload_user_avatar,
|
upload_user_avatar,
|
||||||
upload_user_banner,
|
upload_user_banner,
|
||||||
},
|
},
|
||||||
|
@ -181,7 +183,9 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
scope("/site")
|
scope("/site")
|
||||||
.route("", get().to(get_site_v4))
|
.route("", get().to(get_site_v4))
|
||||||
.route("", post().to(create_site))
|
.route("", post().to(create_site))
|
||||||
.route("", put().to(update_site)),
|
.route("", put().to(update_site))
|
||||||
|
.route("/icon", post().to(upload_site_icon))
|
||||||
|
.route("/banner", post().to(upload_site_banner)),
|
||||||
)
|
)
|
||||||
.route("/modlog", get().to(get_mod_log))
|
.route("/modlog", get().to(get_mod_log))
|
||||||
.service(
|
.service(
|
||||||
|
|
Loading…
Reference in a new issue