2 check_is_apub_id_valid,
3 extensions::signatures::verify,
4 fetcher::get_or_fetch_and_upsert_user,
9 activity::{ActorAndObject, Follow, Undo},
13 use actix_web::{web, HttpRequest, HttpResponse};
14 use anyhow::{anyhow, Context};
16 community::{Community, CommunityFollower, CommunityFollowerForm},
20 use lemmy_structs::blocking;
21 use lemmy_utils::{location_info, LemmyError};
22 use lemmy_websocket::LemmyContext;
24 use serde::{Deserialize, Serialize};
27 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
28 #[serde(rename_all = "PascalCase")]
34 pub type AcceptedActivities = ActorAndObject<ValidTypes>;
36 /// Handler for all incoming activities to community inboxes.
37 pub async fn community_inbox(
39 input: web::Json<AcceptedActivities>,
40 path: web::Path<String>,
41 context: web::Data<LemmyContext>,
42 ) -> Result<HttpResponse, LemmyError> {
43 let activity = input.into_inner();
45 let path = path.into_inner();
46 let community = blocking(&context.pool(), move |conn| {
47 Community::read_from_name(&conn, &path)
54 "Received activity is addressed to remote community {}",
61 "Community {} received activity {:?}",
62 &community.name, &activity
64 let user_uri = activity
66 .as_single_xsd_any_uri()
67 .context(location_info!())?;
68 check_is_apub_id_valid(user_uri)?;
70 let user = get_or_fetch_and_upsert_user(&user_uri, &context).await?;
72 verify(&request, &user)?;
74 let any_base = activity.clone().into_any_base()?;
75 let kind = activity.kind().context(location_info!())?;
76 let user_id = user.id;
77 let res = match kind {
78 ValidTypes::Follow => handle_follow(any_base, user, community, &context).await,
79 ValidTypes::Undo => handle_undo_follow(any_base, user, community, &context).await,
82 insert_activity(user_id, activity.clone(), false, context.pool()).await?;
86 /// Handle a follow request from a remote user, adding it to the local database and returning an
88 async fn handle_follow(
92 context: &LemmyContext,
93 ) -> Result<HttpResponse, LemmyError> {
94 let follow = Follow::from_any_base(activity)?.context(location_info!())?;
95 let community_follower_form = CommunityFollowerForm {
96 community_id: community.id,
100 // This will fail if they're already a follower, but ignore the error.
101 blocking(&context.pool(), move |conn| {
102 CommunityFollower::follow(&conn, &community_follower_form).ok()
106 community.send_accept_follow(follow, context).await?;
108 Ok(HttpResponse::Ok().finish())
111 async fn handle_undo_follow(
114 community: Community,
115 context: &LemmyContext,
116 ) -> Result<HttpResponse, LemmyError> {
117 let _undo = Undo::from_any_base(activity)?.context(location_info!())?;
119 let community_follower_form = CommunityFollowerForm {
120 community_id: community.id,
124 // This will fail if they aren't a follower, but ignore the error.
125 blocking(&context.pool(), move |conn| {
126 CommunityFollower::unfollow(&conn, &community_follower_form).ok()
130 Ok(HttpResponse::Ok().finish())