]> Untitled Git - lemmy.git/blob - server/src/apub/user.rs
Some GetUserDetails cleanup.
[lemmy.git] / server / src / apub / user.rs
1 use crate::{
2   apub::{
3     activities::send_activity,
4     create_apub_response,
5     insert_activity,
6     ActorType,
7     FromApub,
8     PersonExt,
9     ToApub,
10   },
11   blocking,
12   routes::DbPoolParam,
13   DbPool,
14   LemmyError,
15 };
16 use activitystreams_ext::Ext1;
17 use activitystreams_new::{
18   activity::{Follow, Undo},
19   actor::{ApActor, Endpoints, Person},
20   context,
21   object::{Image, Tombstone},
22   prelude::*,
23 };
24 use actix_web::{body::Body, client::Client, web, HttpResponse};
25 use lemmy_db::{
26   naive_now,
27   user::{UserForm, User_},
28 };
29 use lemmy_utils::convert_datetime;
30 use serde::Deserialize;
31 use url::Url;
32
33 #[derive(Deserialize)]
34 pub struct UserQuery {
35   user_name: String,
36 }
37
38 #[async_trait::async_trait(?Send)]
39 impl ToApub for User_ {
40   type Response = PersonExt;
41
42   // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
43   async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
44     // TODO go through all these to_string and to_owned()
45     let mut person = Person::new();
46     person
47       .set_context(context())
48       .set_id(Url::parse(&self.actor_id)?)
49       .set_name(self.name.to_owned())
50       .set_published(convert_datetime(self.published));
51
52     if let Some(u) = self.updated {
53       person.set_updated(convert_datetime(u));
54     }
55
56     if let Some(avatar_url) = &self.avatar {
57       let mut image = Image::new();
58       image.set_url(avatar_url.to_owned());
59       person.set_icon(image.into_any_base()?);
60     }
61
62     let mut ap_actor = ApActor::new(self.get_inbox_url().parse()?, person);
63     ap_actor
64       .set_outbox(self.get_outbox_url().parse()?)
65       .set_followers(self.get_followers_url().parse()?)
66       .set_following(self.get_following_url().parse()?)
67       .set_liked(self.get_liked_url().parse()?)
68       .set_endpoints(Endpoints {
69         shared_inbox: Some(self.get_shared_inbox_url().parse()?),
70         ..Default::default()
71       });
72
73     if let Some(i) = &self.preferred_username {
74       ap_actor.set_preferred_username(i.to_owned());
75     }
76
77     Ok(Ext1::new(ap_actor, self.get_public_key_ext()))
78   }
79   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
80     unimplemented!()
81   }
82 }
83
84 #[async_trait::async_trait(?Send)]
85 impl ActorType for User_ {
86   fn actor_id_str(&self) -> String {
87     self.actor_id.to_owned()
88   }
89
90   fn public_key(&self) -> String {
91     self.public_key.to_owned().unwrap()
92   }
93
94   fn private_key(&self) -> String {
95     self.private_key.to_owned().unwrap()
96   }
97
98   /// As a given local user, send out a follow request to a remote community.
99   async fn send_follow(
100     &self,
101     follow_actor_id: &str,
102     client: &Client,
103     pool: &DbPool,
104   ) -> Result<(), LemmyError> {
105     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
106     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
107     follow.set_context(context()).set_id(id.parse()?);
108     let to = format!("{}/inbox", follow_actor_id);
109
110     insert_activity(self.id, follow.clone(), true, pool).await?;
111
112     send_activity(client, &follow.into_any_base()?, self, vec![to]).await?;
113     Ok(())
114   }
115
116   async fn send_unfollow(
117     &self,
118     follow_actor_id: &str,
119     client: &Client,
120     pool: &DbPool,
121   ) -> Result<(), LemmyError> {
122     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
123     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
124     follow.set_context(context()).set_id(id.parse()?);
125
126     let to = format!("{}/inbox", follow_actor_id);
127
128     // TODO
129     // Undo that fake activity
130     let undo_id = format!("{}/undo/follow/{}", self.actor_id, uuid::Uuid::new_v4());
131     let mut undo = Undo::new(Url::parse(&self.actor_id)?, follow.into_any_base()?);
132     undo.set_context(context()).set_id(undo_id.parse()?);
133
134     insert_activity(self.id, undo.clone(), true, pool).await?;
135
136     send_activity(client, &undo.into_any_base()?, self, vec![to]).await?;
137     Ok(())
138   }
139
140   async fn send_delete(
141     &self,
142     _creator: &User_,
143     _client: &Client,
144     _pool: &DbPool,
145   ) -> Result<(), LemmyError> {
146     unimplemented!()
147   }
148
149   async fn send_undo_delete(
150     &self,
151     _creator: &User_,
152     _client: &Client,
153     _pool: &DbPool,
154   ) -> Result<(), LemmyError> {
155     unimplemented!()
156   }
157
158   async fn send_remove(
159     &self,
160     _creator: &User_,
161     _client: &Client,
162     _pool: &DbPool,
163   ) -> Result<(), LemmyError> {
164     unimplemented!()
165   }
166
167   async fn send_undo_remove(
168     &self,
169     _creator: &User_,
170     _client: &Client,
171     _pool: &DbPool,
172   ) -> Result<(), LemmyError> {
173     unimplemented!()
174   }
175
176   async fn send_accept_follow(
177     &self,
178     _follow: Follow,
179     _client: &Client,
180     _pool: &DbPool,
181   ) -> Result<(), LemmyError> {
182     unimplemented!()
183   }
184
185   async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<String>, LemmyError> {
186     unimplemented!()
187   }
188 }
189
190 #[async_trait::async_trait(?Send)]
191 impl FromApub for UserForm {
192   type ApubType = PersonExt;
193   /// Parse an ActivityPub person received from another instance into a Lemmy user.
194   async fn from_apub(
195     person: &PersonExt,
196     _: &Client,
197     _: &DbPool,
198     actor_id: &Url,
199   ) -> Result<Self, LemmyError> {
200     let avatar = match person.icon() {
201       Some(any_image) => Image::from_any_base(any_image.as_one().unwrap().clone())
202         .unwrap()
203         .unwrap()
204         .url()
205         .unwrap()
206         .as_single_xsd_any_uri()
207         .map(|u| u.to_string()),
208       None => None,
209     };
210
211     Ok(UserForm {
212       name: person
213         .name()
214         .unwrap()
215         .one()
216         .unwrap()
217         .as_xsd_string()
218         .unwrap()
219         .to_string(),
220       preferred_username: person.inner.preferred_username().map(|u| u.to_string()),
221       password_encrypted: "".to_string(),
222       admin: false,
223       banned: false,
224       email: None,
225       avatar,
226       updated: person.updated().map(|u| u.to_owned().naive_local()),
227       show_nsfw: false,
228       theme: "".to_string(),
229       default_sort_type: 0,
230       default_listing_type: 0,
231       lang: "".to_string(),
232       show_avatars: false,
233       send_notifications_to_email: false,
234       matrix_user_id: None,
235       actor_id: person.id(actor_id.domain().unwrap())?.unwrap().to_string(),
236       bio: person
237         .inner
238         .summary()
239         .map(|s| s.as_single_xsd_string().unwrap().into()),
240       local: false,
241       private_key: None,
242       public_key: Some(person.ext_one.public_key.to_owned().public_key_pem),
243       last_refreshed_at: Some(naive_now()),
244     })
245   }
246 }
247
248 /// Return the user json over HTTP.
249 pub async fn get_apub_user_http(
250   info: web::Path<UserQuery>,
251   db: DbPoolParam,
252 ) -> Result<HttpResponse<Body>, LemmyError> {
253   let user_name = info.into_inner().user_name;
254   let user = blocking(&db, move |conn| {
255     User_::find_by_email_or_username(conn, &user_name)
256   })
257   .await??;
258   let u = user.to_apub(&db).await?;
259   Ok(create_apub_response(&u))
260 }