]> Untitled Git - lemmy.git/blob - lemmy_apub/src/inbox/community_inbox.rs
Move websocket code into workspace (#107)
[lemmy.git] / lemmy_apub / src / inbox / community_inbox.rs
1 use crate::{
2   check_is_apub_id_valid,
3   extensions::signatures::verify,
4   fetcher::get_or_fetch_and_upsert_user,
5   insert_activity,
6   ActorType,
7 };
8 use activitystreams::{
9   activity::{ActorAndObject, Follow, Undo},
10   base::AnyBase,
11   prelude::*,
12 };
13 use actix_web::{web, HttpRequest, HttpResponse};
14 use anyhow::{anyhow, Context};
15 use lemmy_db::{
16   community::{Community, CommunityFollower, CommunityFollowerForm},
17   user::User_,
18   Followable,
19 };
20 use lemmy_structs::blocking;
21 use lemmy_utils::{location_info, LemmyError};
22 use lemmy_websocket::LemmyContext;
23 use log::debug;
24 use serde::{Deserialize, Serialize};
25 use std::fmt::Debug;
26
27 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
28 #[serde(rename_all = "PascalCase")]
29 pub enum ValidTypes {
30   Follow,
31   Undo,
32 }
33
34 pub type AcceptedActivities = ActorAndObject<ValidTypes>;
35
36 /// Handler for all incoming activities to community inboxes.
37 pub async fn community_inbox(
38   request: HttpRequest,
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();
44
45   let path = path.into_inner();
46   let community = blocking(&context.pool(), move |conn| {
47     Community::read_from_name(&conn, &path)
48   })
49   .await??;
50
51   if !community.local {
52     return Err(
53       anyhow!(
54         "Received activity is addressed to remote community {}",
55         &community.actor_id
56       )
57       .into(),
58     );
59   }
60   debug!(
61     "Community {} received activity {:?}",
62     &community.name, &activity
63   );
64   let user_uri = activity
65     .actor()?
66     .as_single_xsd_any_uri()
67     .context(location_info!())?;
68   check_is_apub_id_valid(user_uri)?;
69
70   let user = get_or_fetch_and_upsert_user(&user_uri, &context).await?;
71
72   verify(&request, &user)?;
73
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,
80   };
81
82   insert_activity(user_id, activity.clone(), false, context.pool()).await?;
83   res
84 }
85
86 /// Handle a follow request from a remote user, adding it to the local database and returning an
87 /// Accept activity.
88 async fn handle_follow(
89   activity: AnyBase,
90   user: User_,
91   community: Community,
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,
97     user_id: user.id,
98   };
99
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()
103   })
104   .await?;
105
106   community.send_accept_follow(follow, context).await?;
107
108   Ok(HttpResponse::Ok().finish())
109 }
110
111 async fn handle_undo_follow(
112   activity: AnyBase,
113   user: User_,
114   community: Community,
115   context: &LemmyContext,
116 ) -> Result<HttpResponse, LemmyError> {
117   let _undo = Undo::from_any_base(activity)?.context(location_info!())?;
118
119   let community_follower_form = CommunityFollowerForm {
120     community_id: community.id,
121     user_id: user.id,
122   };
123
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()
127   })
128   .await?;
129
130   Ok(HttpResponse::Ok().finish())
131 }