@ -1,10 +1,11 @@
use crate ::PerformCrud ;
use actix_web ::web ::Data ;
use activitypub_federation ::config ::Data ;
use actix_web ::web ::Json ;
use lemmy_api_common ::{
build_response ::build_post_response ,
context ::LemmyContext ,
post ::{ CreatePost , PostResponse } ,
request ::fetch_site_data ,
send_activity ::{ ActivityChannel , SendActivityData } ,
utils ::{
check_community_ban ,
check_community_deleted_or_removed ,
@ -40,147 +41,145 @@ use tracing::Instrument;
use url ::Url ;
use webmention ::{ Webmention , WebmentionError } ;
#[ async_trait::async_trait(?Send) ]
impl PerformCrud for CreatePost {
type Response = PostResponse ;
#[ tracing::instrument(skip(context)) ]
async fn perform ( & self , context : & Data < LemmyContext > ) -> Result < PostResponse , LemmyError > {
let data : & CreatePost = self ;
let local_user_view = local_user_view_from_jwt ( & data . auth , context ) . await ? ;
let local_site = LocalSite ::read ( & mut context . pool ( ) ) . await ? ;
let slur_regex = local_site_to_slur_regex ( & local_site ) ;
check_slurs ( & data . name , & slur_regex ) ? ;
check_slurs_opt ( & data . body , & slur_regex ) ? ;
honeypot_check ( & data . honeypot ) ? ;
let data_url = data . url . as_ref ( ) ;
let url = data_url . map ( clean_url_params ) . map ( Into ::into ) ; // TODO no good way to handle a "clear"
is_valid_post_title ( & data . name ) ? ;
is_valid_body_field ( & data . body , true ) ? ;
check_url_scheme ( & data . url ) ? ;
check_community_ban (
local_user_view . person . id ,
data . community_id ,
#[ tracing::instrument(skip(context)) ]
pub async fn create_post (
data : Json < CreatePost > ,
context : Data < LemmyContext > ,
) -> Result < Json < PostResponse > , LemmyError > {
let local_user_view = local_user_view_from_jwt ( & data . auth , & context ) . await ? ;
let local_site = LocalSite ::read ( & mut context . pool ( ) ) . await ? ;
let slur_regex = local_site_to_slur_regex ( & local_site ) ;
check_slurs ( & data . name , & slur_regex ) ? ;
check_slurs_opt ( & data . body , & slur_regex ) ? ;
honeypot_check ( & data . honeypot ) ? ;
let data_url = data . url . as_ref ( ) ;
let url = data_url . map ( clean_url_params ) . map ( Into ::into ) ; // TODO no good way to handle a "clear"
is_valid_post_title ( & data . name ) ? ;
is_valid_body_field ( & data . body , true ) ? ;
check_url_scheme ( & data . url ) ? ;
check_community_ban (
local_user_view . person . id ,
data . community_id ,
& mut context . pool ( ) ,
)
. await ? ;
check_community_deleted_or_removed ( data . community_id , & mut context . pool ( ) ) . await ? ;
let community_id = data . community_id ;
let community = Community ::read ( & mut context . pool ( ) , community_id ) . await ? ;
if community . posting_restricted_to_mods {
let community_id = data . community_id ;
let is_mod = CommunityView ::is_mod_or_admin (
& mut context . pool ( ) ,
local_user_view . local_user . person_id ,
community_id ,
)
. await ? ;
check_community_deleted_or_removed ( data . community_id , & mut context . pool ( ) ) . await ? ;
if ! is_mod {
return Err ( LemmyErrorType ::OnlyModsCanPostInCommunity ) ? ;
}
}
let community_id = data . community_id ;
let community = Community ::read ( & mut context . pool ( ) , community_id ) . await ? ;
if community . posting_restricted_to_mods {
let community_id = data . community_id ;
let is_mod = CommunityView ::is_mod_or_admin (
// Fetch post links and pictrs cached image
let ( metadata_res , thumbnail_url ) =
fetch_site_data ( context . client ( ) , context . settings ( ) , data_url , true ) . await ;
let ( embed_title , embed_description , embed_video_url ) = metadata_res
. map ( | u | ( u . title , u . description , u . embed_video_url ) )
. unwrap_or_default ( ) ;
let language_id = match data . language_id {
Some ( lid ) = > Some ( lid ) ,
None = > {
default_post_language (
& mut context . pool ( ) ,
local_user_view . local_user . person_id ,
community_id ,
local_user_view . local_user . id ,
)
. await ? ;
if ! is_mod {
return Err ( LemmyErrorType ::OnlyModsCanPostInCommunity ) ? ;
}
. await ?
}
// Fetch post links and pictrs cached image
let ( metadata_res , thumbnail_url ) =
fetch_site_data ( context . client ( ) , context . settings ( ) , data_url , true ) . await ;
let ( embed_title , embed_description , embed_video_url ) = metadata_res
. map ( | u | ( u . title , u . description , u . embed_video_url ) )
. unwrap_or_default ( ) ;
let language_id = match data . language_id {
Some ( lid ) = > Some ( lid ) ,
None = > {
default_post_language (
& mut context . pool ( ) ,
community_id ,
local_user_view . local_user . id ,
)
. await ?
}
} ;
CommunityLanguage ::is_allowed_community_language (
& mut context . pool ( ) ,
language_id ,
community_id ,
)
} ;
CommunityLanguage ::is_allowed_community_language ( & mut context . pool ( ) , language_id , community_id )
. await ? ;
let post_form = PostInsertForm ::builder ( )
. name ( data . name . trim ( ) . to_owned ( ) )
. url ( url )
. body ( data . body . clone ( ) )
. community_id ( data . community_id )
. creator_id ( local_user_view . person . id )
. nsfw ( data . nsfw )
. embed_title ( embed_title )
. embed_description ( embed_description )
. embed_video_url ( embed_video_url )
. language_id ( language_id )
. thumbnail_url ( thumbnail_url )
. build ( ) ;
let inserted_post = Post ::create ( & mut context . pool ( ) , & post_form )
. await
. with_lemmy_type ( LemmyErrorType ::CouldntCreatePost ) ? ;
let inserted_post_id = inserted_post . id ;
let protocol_and_hostname = context . settings ( ) . get_protocol_and_hostname ( ) ;
let apub_id = generate_local_apub_endpoint (
EndpointType ::Post ,
& inserted_post_id . to_string ( ) ,
& protocol_and_hostname ,
) ? ;
let updated_post = Post ::update (
& mut context . pool ( ) ,
inserted_post_id ,
& PostUpdateForm ::builder ( ) . ap_id ( Some ( apub_id ) ) . build ( ) ,
)
let post_form = PostInsertForm ::builder ( )
. name ( data . name . trim ( ) . to_owned ( ) )
. url ( url )
. body ( data . body . clone ( ) )
. community_id ( data . community_id )
. creator_id ( local_user_view . person . id )
. nsfw ( data . nsfw )
. embed_title ( embed_title )
. embed_description ( embed_description )
. embed_video_url ( embed_video_url )
. language_id ( language_id )
. thumbnail_url ( thumbnail_url )
. build ( ) ;
let inserted_post = Post ::create ( & mut context . pool ( ) , & post_form )
. await
. with_lemmy_type ( LemmyErrorType ::CouldntCreatePost ) ? ;
// They like their own post by default
let person_id = local_user_view . person . id ;
let post_id = inserted_post . id ;
let like_form = PostLikeForm {
post_id ,
person_id ,
score : 1 ,
} ;
let inserted_post_id = inserted_post . id ;
let protocol_and_hostname = context . settings ( ) . get_protocol_and_hostname ( ) ;
let apub_id = generate_local_apub_endpoint (
EndpointType ::Post ,
& inserted_post_id . to_string ( ) ,
& protocol_and_hostname ,
) ? ;
let updated_post = Post ::update (
& mut context . pool ( ) ,
inserted_post_id ,
& PostUpdateForm ::builder ( ) . ap_id ( Some ( apub_id ) ) . build ( ) ,
)
. await
. with_lemmy_type ( LemmyErrorType ::CouldntCreatePost ) ? ;
// They like their own post by default
let person_id = local_user_view . person . id ;
let post_id = inserted_post . id ;
let like_form = PostLikeForm {
post_id ,
person_id ,
score : 1 ,
} ;
PostLike ::like ( & mut context . pool ( ) , & like_form )
. await
. with_lemmy_type ( LemmyErrorType ::CouldntLikePost ) ? ;
ActivityChannel ::submit_activity ( SendActivityData ::CreatePost ( updated_post . clone ( ) ) , & context )
. await ? ;
PostLike ::like ( & mut context . pool ( ) , & like_form )
. await
. with_lemmy_type ( LemmyErrorType ::CouldntLikePost ) ? ;
// Mark the post as read
mark_post_as_read ( person_id , post_id , & mut context . pool ( ) ) . await ? ;
if let Some ( url ) = updated_post . url . clone ( ) {
let task = async move {
let mut webmention =
Webmention ::new ::< Url > ( updated_post . ap_id . clone ( ) . into ( ) , url . clone ( ) . into ( ) ) ? ;
webmention . set_checked ( true ) ;
match webmention
. send ( )
. instrument ( tracing ::info_span ! ( "Sending webmention" ) )
. await
{
Err ( WebmentionError ::NoEndpointDiscovered ( _ ) ) = > Ok ( ( ) ) ,
Ok ( _ ) = > Ok ( ( ) ) ,
Err ( e ) = > Err ( e ) . with_lemmy_type ( LemmyErrorType ::CouldntSendWebmention ) ,
}
} ;
if * SYNCHRONOUS_FEDERATION {
task . await ? ;
} else {
spawn_try_task ( task ) ;
// Mark the post as read
mark_post_as_read ( person_id , post_id , & mut context . pool ( ) ) . await ? ;
if let Some ( url ) = updated_post . url . clone ( ) {
let task = async move {
let mut webmention =
Webmention ::new ::< Url > ( updated_post . ap_id . clone ( ) . into ( ) , url . clone ( ) . into ( ) ) ? ;
webmention . set_checked ( true ) ;
match webmention
. send ( )
. instrument ( tracing ::info_span ! ( "Sending webmention" ) )
. await
{
Err ( WebmentionError ::NoEndpointDiscovered ( _ ) ) = > Ok ( ( ) ) ,
Ok ( _ ) = > Ok ( ( ) ) ,
Err ( e ) = > Err ( e ) . with_lemmy_type ( LemmyErrorType ::CouldntSendWebmention ) ,
}
} ;
if * SYNCHRONOUS_FEDERATION {
task . await ? ;
} else {
spawn_try_task ( task ) ;
}
} ;
build_post_response ( context , community_id , person_id , post_id ) . await
}
Ok ( Json (
build_post_response ( & context , community_id , person_id , post_id ) . await ? ,
) )
}