3 check_is_apub_id_valid,
4 extensions::signatures::verify,
5 fetcher::get_or_fetch_and_upsert_user,
11 use activitystreams::{
12 activity::{ActorAndObject, Follow, Undo},
16 use actix_web::{web, HttpRequest, HttpResponse};
17 use anyhow::{anyhow, Context};
18 use lemmy_structs::blocking;
20 community::{Community, CommunityFollower, CommunityFollowerForm},
24 use lemmy_utils::{location_info, LemmyError};
26 use serde::{Deserialize, Serialize};
29 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
30 #[serde(rename_all = "PascalCase")]
36 pub type AcceptedActivities = ActorAndObject<ValidTypes>;
38 /// Handler for all incoming activities to community inboxes.
39 pub async fn community_inbox(
41 input: web::Json<AcceptedActivities>,
42 path: web::Path<String>,
43 context: web::Data<LemmyContext>,
44 ) -> Result<HttpResponse, LemmyError> {
45 let activity = input.into_inner();
47 let path = path.into_inner();
48 let community = blocking(&context.pool(), move |conn| {
49 Community::read_from_name(&conn, &path)
56 "Received activity is addressed to remote community {}",
63 "Community {} received activity {:?}",
64 &community.name, &activity
66 let user_uri = activity
68 .as_single_xsd_any_uri()
69 .context(location_info!())?;
70 check_is_apub_id_valid(user_uri)?;
72 let user = get_or_fetch_and_upsert_user(&user_uri, &context).await?;
74 verify(&request, &user)?;
76 let any_base = activity.clone().into_any_base()?;
77 let kind = activity.kind().context(location_info!())?;
78 let user_id = user.id;
79 let res = match kind {
80 ValidTypes::Follow => handle_follow(any_base, user, community, &context).await,
81 ValidTypes::Undo => handle_undo_follow(any_base, user, community, &context).await,
84 insert_activity(user_id, activity.clone(), false, context.pool()).await?;
88 /// Handle a follow request from a remote user, adding it to the local database and returning an
90 async fn handle_follow(
94 context: &LemmyContext,
95 ) -> Result<HttpResponse, LemmyError> {
96 let follow = Follow::from_any_base(activity)?.context(location_info!())?;
97 let community_follower_form = CommunityFollowerForm {
98 community_id: community.id,
102 // This will fail if they're already a follower, but ignore the error.
103 blocking(&context.pool(), move |conn| {
104 CommunityFollower::follow(&conn, &community_follower_form).ok()
108 community.send_accept_follow(follow, context).await?;
110 Ok(HttpResponse::Ok().finish())
113 async fn handle_undo_follow(
116 community: Community,
117 context: &LemmyContext,
118 ) -> Result<HttpResponse, LemmyError> {
119 let _undo = Undo::from_any_base(activity)?.context(location_info!())?;
121 let community_follower_form = CommunityFollowerForm {
122 community_id: community.id,
126 // This will fail if they aren't a follower, but ignore the error.
127 blocking(&context.pool(), move |conn| {
128 CommunityFollower::unfollow(&conn, &community_follower_form).ok()
132 Ok(HttpResponse::Ok().finish())