]> Untitled Git - lemmy.git/blobdiff - crates/apub/src/objects/mod.rs
Implement instance actor (#1798)
[lemmy.git] / crates / apub / src / objects / mod.rs
index 2114a1d89ae8436267edb2ae92265668fbeea710..d7e386b1063bdadb5b68f8c1797e2d4fa92991d9 100644 (file)
@@ -1,85 +1,99 @@
-use crate::fetcher::person::get_or_fetch_and_upsert_person;
-use activitystreams::{
-  base::BaseExt,
-  object::{kind::ImageType, Tombstone, TombstoneExt},
-};
-use anyhow::anyhow;
-use chrono::NaiveDateTime;
-use lemmy_apub_lib::values::MediaTypeMarkdown;
-use lemmy_db_queries::DbPool;
-use lemmy_utils::{utils::convert_datetime, LemmyError};
-use lemmy_websocket::LemmyContext;
-use url::Url;
+use crate::protocol::Source;
+use html2md::parse_html;
 
-pub(crate) mod comment;
-pub(crate) mod community;
-pub(crate) mod person;
-pub(crate) mod post;
-pub(crate) mod private_message;
+pub mod comment;
+pub mod community;
+pub mod instance;
+pub mod person;
+pub mod post;
+pub mod private_message;
 
-/// Trait for converting an object or actor into the respective ActivityPub type.
-#[async_trait::async_trait(?Send)]
-pub trait ToApub {
-  type ApubType;
-  async fn to_apub(&self, pool: &DbPool) -> Result<Self::ApubType, LemmyError>;
-  fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
+pub(crate) fn get_summary_from_string_or_source(
+  raw: &Option<String>,
+  source: &Option<Source>,
+) -> Option<String> {
+  if let Some(source) = &source {
+    Some(source.content.clone())
+  } else {
+    raw.as_ref().map(|s| parse_html(s))
+  }
 }
 
-#[async_trait::async_trait(?Send)]
-pub trait FromApub {
-  type ApubType;
-  /// Converts an object from ActivityPub type to Lemmy internal type.
-  ///
-  /// * `apub` The object to read from
-  /// * `context` LemmyContext which holds DB pool, HTTP client etc
-  /// * `expected_domain` Domain where the object was received from. None in case of mod action.
-  /// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
-  async fn from_apub(
-    apub: &Self::ApubType,
-    context: &LemmyContext,
-    expected_domain: &Url,
-    request_counter: &mut i32,
-  ) -> Result<Self, LemmyError>
-  where
-    Self: Sized;
-}
+#[cfg(test)]
+pub(crate) mod tests {
+  use actix::Actor;
+  use background_jobs::QueueHandle;
+  use diesel::{
+    r2d2::{ConnectionManager, Pool},
+    PgConnection,
+  };
+  use lemmy_db_schema::{
+    establish_unpooled_connection,
+    get_database_url_from_env,
+    source::secret::Secret,
+  };
+  use lemmy_utils::{
+    rate_limit::{rate_limiter::RateLimiter, RateLimit},
+    request::build_user_agent,
+    settings::structs::Settings,
+    LemmyError,
+  };
+  use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
+  use reqwest::Client;
+  use reqwest_middleware::ClientBuilder;
+  use serde::de::DeserializeOwned;
+  use std::{fs::File, io::BufReader, sync::Arc};
+  use tokio::sync::Mutex;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct Source {
-  content: String,
-  media_type: MediaTypeMarkdown,
-}
+  // TODO: would be nice if we didnt have to use a full context for tests.
+  //       or at least write a helper function so this code is shared with main.rs
+  pub(crate) fn init_context(activity_queue: QueueHandle) -> LemmyContext {
+    // call this to run migrations
+    establish_unpooled_connection();
+    let settings = Settings::init().unwrap();
+    let rate_limiter = RateLimit {
+      rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
+      rate_limit_config: settings.rate_limit.to_owned().unwrap_or_default(),
+    };
+    let client = Client::builder()
+      .user_agent(build_user_agent(&settings))
+      .build()
+      .unwrap();
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ImageObject {
-  #[serde(rename = "type")]
-  kind: ImageType,
-  url: Url,
-}
-
-/// Updated is actually the deletion time
-fn create_tombstone<T>(
-  deleted: bool,
-  object_id: Url,
-  updated: Option<NaiveDateTime>,
-  former_type: T,
-) -> Result<Tombstone, LemmyError>
-where
-  T: ToString,
-{
-  if deleted {
-    if let Some(updated) = updated {
-      let mut tombstone = Tombstone::new();
-      tombstone.set_id(object_id);
-      tombstone.set_former_type(former_type.to_string());
-      tombstone.set_deleted(convert_datetime(updated));
-      Ok(tombstone)
-    } else {
-      Err(anyhow!("Cant convert to tombstone because updated time was None.").into())
+    let client = ClientBuilder::new(client).build();
+    let secret = Secret {
+      id: 0,
+      jwt_secret: "".to_string(),
+    };
+    let db_url = match get_database_url_from_env() {
+      Ok(url) => url,
+      Err(_) => settings.get_database_url(),
+    };
+    let manager = ConnectionManager::<PgConnection>::new(&db_url);
+    let pool = Pool::builder()
+      .max_size(settings.database.pool_size)
+      .build(manager)
+      .unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
+    async fn x() -> Result<String, LemmyError> {
+      Ok("".to_string())
     }
-  } else {
-    Err(anyhow!("Cant convert object to tombstone if it wasnt deleted").into())
+    let chat_server = ChatServer::startup(
+      pool.clone(),
+      rate_limiter,
+      |_, _, _, _| Box::pin(x()),
+      |_, _, _, _| Box::pin(x()),
+      client.clone(),
+      activity_queue.clone(),
+      settings.clone(),
+      secret.clone(),
+    )
+    .start();
+    LemmyContext::create(pool, chat_server, client, activity_queue, settings, secret)
+  }
+
+  pub(crate) fn file_to_json_object<T: DeserializeOwned>(path: &str) -> Result<T, LemmyError> {
+    let file = File::open(path)?;
+    let reader = BufReader::new(file);
+    Ok(serde_json::from_reader(reader)?)
   }
 }