]> Untitled Git - lemmy.git/blob - crates/apub/src/http/mod.rs
7a4a362faa652360e526fb0ceaaf29a0c62f7a32
[lemmy.git] / crates / apub / src / http / mod.rs
1 use crate::{
2   activity_lists::SharedInboxActivities,
3   fetcher::user_or_community::UserOrCommunity,
4   protocol::objects::tombstone::Tombstone,
5   CONTEXT,
6 };
7 use activitypub_federation::{
8   actix_web::inbox::receive_activity,
9   config::Data,
10   protocol::context::WithContext,
11   FEDERATION_CONTENT_TYPE,
12 };
13 use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
14 use http::StatusCode;
15 use lemmy_api_common::context::LemmyContext;
16 use lemmy_db_schema::source::activity::Activity;
17 use lemmy_utils::error::{LemmyError, LemmyResult};
18 use serde::{Deserialize, Serialize};
19 use std::ops::Deref;
20 use url::Url;
21
22 mod comment;
23 mod community;
24 mod person;
25 mod post;
26 pub mod routes;
27 pub mod site;
28
29 pub async fn shared_inbox(
30   request: HttpRequest,
31   body: Bytes,
32   data: Data<LemmyContext>,
33 ) -> LemmyResult<HttpResponse> {
34   receive_activity::<SharedInboxActivities, UserOrCommunity, LemmyContext>(request, body, &data)
35     .await
36 }
37
38 /// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
39 /// headers.
40 ///
41 /// actix-web doesn't allow pretty-print for json so we need to do this manually.
42 fn create_apub_response<T>(data: &T) -> LemmyResult<HttpResponse>
43 where
44   T: Serialize,
45 {
46   let json = serde_json::to_string_pretty(&WithContext::new(data, CONTEXT.clone()))?;
47
48   Ok(
49     HttpResponse::Ok()
50       .content_type(FEDERATION_CONTENT_TYPE)
51       .content_type("application/json")
52       .body(json),
53   )
54 }
55
56 fn create_apub_tombstone_response<T: Into<Url>>(id: T) -> LemmyResult<HttpResponse> {
57   let tombstone = Tombstone::new(id.into());
58   let json = serde_json::to_string_pretty(&WithContext::new(tombstone, CONTEXT.deref().clone()))?;
59
60   Ok(
61     HttpResponse::Gone()
62       .content_type(FEDERATION_CONTENT_TYPE)
63       .status(StatusCode::GONE)
64       .content_type("application/json")
65       .body(json),
66   )
67 }
68
69 fn err_object_not_local() -> LemmyError {
70   LemmyError::from_message("Object not local, fetch it from original instance")
71 }
72
73 #[derive(Deserialize)]
74 pub struct ActivityQuery {
75   type_: String,
76   id: String,
77 }
78
79 /// Return the ActivityPub json representation of a local activity over HTTP.
80 #[tracing::instrument(skip_all)]
81 pub(crate) async fn get_activity(
82   info: web::Path<ActivityQuery>,
83   context: web::Data<LemmyContext>,
84 ) -> Result<HttpResponse, LemmyError> {
85   let settings = context.settings();
86   let activity_id = Url::parse(&format!(
87     "{}/activities/{}/{}",
88     settings.get_protocol_and_hostname(),
89     info.type_,
90     info.id
91   ))?
92   .into();
93   let activity = Activity::read_from_apub_id(context.pool(), &activity_id).await?;
94
95   let sensitive = activity.sensitive.unwrap_or(true);
96   if !activity.local {
97     Err(err_object_not_local())
98   } else if sensitive {
99     Ok(HttpResponse::Forbidden().finish())
100   } else {
101     create_apub_response(&activity.data)
102   }
103 }