]> Untitled Git - lemmy.git/commitdiff
Add correct ActivityPub types conversion for Community and Post.
authorLyra <teromene@teromene.fr>
Thu, 19 Dec 2019 21:59:13 +0000 (22:59 +0100)
committerFelix Ableitner <me@nutomic.com>
Fri, 27 Dec 2019 16:25:20 +0000 (17:25 +0100)
server/src/api/comment.rs
server/src/apub.rs [deleted file]
server/src/apub/community.rs [new file with mode: 0644]
server/src/apub/mod.rs [new file with mode: 0644]
server/src/apub/post.rs [new file with mode: 0644]
server/src/apub/user.rs [new file with mode: 0644]
server/src/lib.rs
server/src/main.rs

index 9f9d46d3096ec098a9c5781e13d45c67fca6726e..9a057f8064f2950a14c2b865a73ea6af4036c25a 100644 (file)
@@ -328,7 +328,7 @@ impl Perform<CommentResponse> for Oper<CreateCommentLike> {
     CommentLike::remove(&conn, &like_form)?;
 
     // Only add the like if the score isnt 0
-    let do_add = &like_form.score != &0 && (&like_form.score == &1 || &like_form.score == &-1);
+    let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
     if do_add {
       let _inserted_like = match CommentLike::like(&conn, &like_form) {
         Ok(like) => like,
diff --git a/server/src/apub.rs b/server/src/apub.rs
deleted file mode 100644 (file)
index 66878d3..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-extern crate activitypub;
-use self::activitypub::{actor::Person, context};
-use crate::db::user::User_;
-
-impl User_ {
-  pub fn person(&self) -> Person {
-    use crate::{to_datetime_utc, Settings};
-    let base_url = &format!("{}/user/{}", Settings::get().api_endpoint(), self.name);
-    let mut person = Person::default();
-    person.object_props.set_context_object(context()).ok();
-    person.object_props.set_id_string(base_url.to_string()).ok();
-    person
-      .object_props
-      .set_name_string(self.name.to_owned())
-      .ok();
-    person
-      .object_props
-      .set_published_utctime(to_datetime_utc(self.published))
-      .ok();
-    if let Some(i) = self.updated {
-      person
-        .object_props
-        .set_updated_utctime(to_datetime_utc(i))
-        .ok();
-    }
-    // person.object_props.summary = self.summary;
-
-    person
-      .ap_actor_props
-      .set_inbox_string(format!("{}/inbox", &base_url))
-      .ok();
-    person
-      .ap_actor_props
-      .set_outbox_string(format!("{}/outbox", &base_url))
-      .ok();
-    person
-      .ap_actor_props
-      .set_following_string(format!("{}/following", &base_url))
-      .ok();
-    person
-      .ap_actor_props
-      .set_liked_string(format!("{}/liked", &base_url))
-      .ok();
-    if let Some(i) = &self.preferred_username {
-      person
-        .ap_actor_props
-        .set_preferred_username_string(i.to_string())
-        .ok();
-    }
-
-    person
-  }
-}
-
-#[cfg(test)]
-mod tests {
-  use super::User_;
-  use crate::db::{ListingType, SortType};
-  use crate::naive_now;
-
-  #[test]
-  fn test_person() {
-    let expected_user = User_ {
-      id: 52,
-      name: "thom".into(),
-      fedi_name: "rrf".into(),
-      preferred_username: None,
-      password_encrypted: "here".into(),
-      email: None,
-      icon: None,
-      published: naive_now(),
-      admin: false,
-      banned: false,
-      updated: None,
-      show_nsfw: false,
-      theme: "darkly".into(),
-      default_sort_type: SortType::Hot as i16,
-      default_listing_type: ListingType::Subscribed as i16,
-      lang: "browser".into(),
-    };
-
-    let person = expected_user.person();
-    assert_eq!(
-      "rrr/api/v1/user/thom",
-      person.object_props.id_string().unwrap()
-    );
-    let json = serde_json::to_string_pretty(&person).unwrap();
-    println!("{}", json);
-  }
-}
diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs
new file mode 100644 (file)
index 0000000..3de68b9
--- /dev/null
@@ -0,0 +1,112 @@
+use crate::apub::make_apub_endpoint;
+use crate::db::community::Community;
+use crate::db::community_view::CommunityFollowerView;
+use crate::db::establish_connection;
+use crate::to_datetime_utc;
+use activitypub::{actor::Group, collection::UnorderedCollection, context};
+use actix_web::body::Body;
+use actix_web::web::Path;
+use actix_web::HttpResponse;
+use serde::Deserialize;
+
+impl Community {
+  pub fn as_group(&self) -> Group {
+    let base_url = make_apub_endpoint("c", &self.name);
+
+    let mut group = Group::default();
+
+    group.object_props.set_context_object(context()).ok();
+    group.object_props.set_id_string(base_url.to_string()).ok();
+    group
+      .object_props
+      .set_name_string(self.name.to_owned())
+      .ok();
+    group
+      .object_props
+      .set_published_utctime(to_datetime_utc(self.published))
+      .ok();
+    if let Some(updated) = self.updated {
+      group
+        .object_props
+        .set_updated_utctime(to_datetime_utc(updated))
+        .ok();
+    }
+
+    if let Some(description) = &self.description {
+      group
+        .object_props
+        .set_summary_string(description.to_string())
+        .ok();
+    }
+
+    group
+      .ap_actor_props
+      .set_inbox_string(format!("{}/inbox", &base_url))
+      .ok();
+    group
+      .ap_actor_props
+      .set_outbox_string(format!("{}/outbox", &base_url))
+      .ok();
+    group
+      .ap_actor_props
+      .set_followers_string(format!("{}/followers", &base_url))
+      .ok();
+
+    group
+  }
+
+  pub fn followers_as_collection(&self) -> UnorderedCollection {
+    let base_url = make_apub_endpoint("c", &self.name);
+
+    let mut collection = UnorderedCollection::default();
+    collection.object_props.set_context_object(context()).ok();
+    collection
+      .object_props
+      .set_id_string(base_url.to_string())
+      .ok();
+
+    let connection = establish_connection();
+    //As we are an object, we validated that the community id was valid
+    let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap();
+
+    let ap_followers = community_followers
+      .iter()
+      .map(|follower| make_apub_endpoint("u", &follower.user_name))
+      .collect();
+
+    collection
+      .collection_props
+      .set_items_string_vec(ap_followers)
+      .unwrap();
+    collection
+  }
+}
+
+#[derive(Deserialize)]
+pub struct CommunityQuery {
+  community_name: String,
+}
+
+pub fn get_apub_community(info: Path<CommunityQuery>) -> HttpResponse<Body> {
+  let connection = establish_connection();
+
+  if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
+    HttpResponse::Ok()
+      .content_type("application/activity+json")
+      .body(serde_json::to_string(&community.as_group()).unwrap())
+  } else {
+    HttpResponse::NotFound().finish()
+  }
+}
+
+pub fn get_apub_community_followers(info: Path<CommunityQuery>) -> HttpResponse<Body> {
+  let connection = establish_connection();
+
+  if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) {
+    HttpResponse::Ok()
+      .content_type("application/activity+json")
+      .body(serde_json::to_string(&community.followers_as_collection()).unwrap())
+  } else {
+    HttpResponse::NotFound().finish()
+  }
+}
diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs
new file mode 100644 (file)
index 0000000..9b861f4
--- /dev/null
@@ -0,0 +1,100 @@
+pub mod community;
+pub mod post;
+pub mod user;
+use crate::Settings;
+
+use std::fmt::Display;
+
+#[cfg(test)]
+mod tests {
+  use crate::db::community::Community;
+  use crate::db::post::Post;
+  use crate::db::user::User_;
+  use crate::db::{ListingType, SortType};
+  use crate::{naive_now, Settings};
+
+  #[test]
+  fn test_person() {
+    let user = User_ {
+      id: 52,
+      name: "thom".into(),
+      fedi_name: "rrf".into(),
+      preferred_username: None,
+      password_encrypted: "here".into(),
+      email: None,
+      icon: None,
+      published: naive_now(),
+      admin: false,
+      banned: false,
+      updated: None,
+      show_nsfw: false,
+      theme: "darkly".into(),
+      default_sort_type: SortType::Hot as i16,
+      default_listing_type: ListingType::Subscribed as i16,
+      lang: "browser".into(),
+    };
+
+    let person = user.as_person();
+    assert_eq!(
+      format!("https://{}/federation/u/thom", Settings::get().hostname),
+      person.object_props.id_string().unwrap()
+    );
+  }
+
+  #[test]
+  fn test_community() {
+    let community = Community {
+      id: 42,
+      name: "Test".into(),
+      title: "Test Title".into(),
+      description: Some("Test community".into()),
+      category_id: 32,
+      creator_id: 52,
+      removed: false,
+      published: naive_now(),
+      updated: Some(naive_now()),
+      deleted: false,
+      nsfw: false,
+    };
+
+    let group = community.as_group();
+    assert_eq!(
+      format!("https://{}/federation/c/Test", Settings::get().hostname),
+      group.object_props.id_string().unwrap()
+    );
+  }
+
+  #[test]
+  fn test_post() {
+    let post = Post {
+      id: 62,
+      name: "A test post".into(),
+      url: None,
+      body: None,
+      creator_id: 52,
+      community_id: 42,
+      published: naive_now(),
+      removed: false,
+      locked: false,
+      stickied: false,
+      nsfw: false,
+      deleted: false,
+      updated: None,
+    };
+
+    let page = post.as_page();
+    assert_eq!(
+      format!("https://{}/federation/post/62", Settings::get().hostname),
+      page.object_props.id_string().unwrap()
+    );
+  }
+}
+
+pub fn make_apub_endpoint<S: Display, T: Display>(point: S, value: T) -> String {
+  format!(
+    "https://{}/federation/{}/{}",
+    Settings::get().hostname,
+    point,
+    value
+  )
+}
diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs
new file mode 100644 (file)
index 0000000..1916365
--- /dev/null
@@ -0,0 +1,38 @@
+use crate::apub::make_apub_endpoint;
+use crate::db::post::Post;
+use crate::to_datetime_utc;
+use activitypub::{context, object::Page};
+
+impl Post {
+  pub fn as_page(&self) -> Page {
+    let base_url = make_apub_endpoint("post", self.id);
+    let mut page = Page::default();
+
+    page.object_props.set_context_object(context()).ok();
+    page.object_props.set_id_string(base_url.to_string()).ok();
+    page.object_props.set_name_string(self.name.to_owned()).ok();
+
+    if let Some(body) = &self.body {
+      page.object_props.set_content_string(body.to_owned()).ok();
+    }
+
+    if let Some(url) = &self.url {
+      page.object_props.set_url_string(url.to_owned()).ok();
+    }
+
+    //page.object_props.set_attributed_to_string
+
+    page
+      .object_props
+      .set_published_utctime(to_datetime_utc(self.published))
+      .ok();
+    if let Some(updated) = self.updated {
+      page
+        .object_props
+        .set_updated_utctime(to_datetime_utc(updated))
+        .ok();
+    }
+
+    page
+  }
+}
diff --git a/server/src/apub/user.rs b/server/src/apub/user.rs
new file mode 100644 (file)
index 0000000..9de2c36
--- /dev/null
@@ -0,0 +1,74 @@
+use crate::apub::make_apub_endpoint;
+use crate::db::establish_connection;
+use crate::db::user::User_;
+use crate::to_datetime_utc;
+use activitypub::{actor::Person, context};
+use actix_web::body::Body;
+use actix_web::web::Path;
+use actix_web::HttpResponse;
+use serde::Deserialize;
+
+impl User_ {
+  pub fn as_person(&self) -> Person {
+    let base_url = make_apub_endpoint("u", &self.name);
+    let mut person = Person::default();
+    person.object_props.set_context_object(context()).ok();
+    person.object_props.set_id_string(base_url.to_string()).ok();
+    person
+      .object_props
+      .set_name_string(self.name.to_owned())
+      .ok();
+    person
+      .object_props
+      .set_published_utctime(to_datetime_utc(self.published))
+      .ok();
+    if let Some(updated) = self.updated {
+      person
+        .object_props
+        .set_updated_utctime(to_datetime_utc(updated))
+        .ok();
+    }
+
+    person
+      .ap_actor_props
+      .set_inbox_string(format!("{}/inbox", &base_url))
+      .ok();
+    person
+      .ap_actor_props
+      .set_outbox_string(format!("{}/outbox", &base_url))
+      .ok();
+    person
+      .ap_actor_props
+      .set_following_string(format!("{}/following", &base_url))
+      .ok();
+    person
+      .ap_actor_props
+      .set_liked_string(format!("{}/liked", &base_url))
+      .ok();
+    if let Some(i) = &self.preferred_username {
+      person
+        .ap_actor_props
+        .set_preferred_username_string(i.to_string())
+        .ok();
+    }
+
+    person
+  }
+}
+
+#[derive(Deserialize)]
+pub struct UserQuery {
+  user_name: String,
+}
+
+pub fn get_apub_user(info: Path<UserQuery>) -> HttpResponse<Body> {
+  let connection = establish_connection();
+
+  if let Ok(user) = User_::find_by_email_or_username(&connection, &info.user_name) {
+    HttpResponse::Ok()
+      .content_type("application/activity+json")
+      .body(serde_json::to_string(&user.as_person()).unwrap())
+  } else {
+    HttpResponse::NotFound().finish()
+  }
+}
index c2fa2189dbf83b8f2cf0a6c00e74efb410e475b9..b560edf4b8cd6c5656032d9f989906ccb3e67605 100644 (file)
@@ -121,9 +121,6 @@ impl Settings {
       email_config,
     }
   }
-  fn api_endpoint(&self) -> String {
-    format!("{}/api/v1", self.hostname)
-  }
 }
 
 pub fn to_datetime_utc(ndt: NaiveDateTime) -> DateTime<Utc> {
@@ -210,10 +207,6 @@ pub fn send_email(
 #[cfg(test)]
 mod tests {
   use crate::{extract_usernames, has_slurs, is_email_regex, remove_slurs, Settings};
-  #[test]
-  fn test_api() {
-    assert_eq!(Settings::get().api_endpoint(), "rrr/api/v1");
-  }
 
   #[test]
   fn test_email() {
index 98ce8e3fd4d2def5e4c8727b50f9f608dfee5e38..d19ef777ea13e156528fce432914b8b54f5ba700 100644 (file)
@@ -6,6 +6,7 @@ use actix::prelude::*;
 use actix_files::NamedFile;
 use actix_web::*;
 use actix_web_actors::ws;
+use lemmy_server::apub;
 use lemmy_server::db::establish_connection;
 use lemmy_server::feeds;
 use lemmy_server::nodeinfo;
@@ -243,6 +244,19 @@ fn main() {
       // RSS
       .route("/feeds/{type}/{name}.xml", web::get().to(feeds::get_feed))
       .route("/feeds/all.xml", web::get().to(feeds::get_all_feed))
+      // Federation
+      .route(
+        "/federation/c/{community_name}",
+        web::get().to(apub::community::get_apub_community),
+      )
+      .route(
+        "/federation/c/{community_name}/followers",
+        web::get().to(apub::community::get_apub_community_followers),
+      )
+      .route(
+        "/federation/u/{user_name}",
+        web::get().to(apub::user::get_apub_user),
+      )
   })
   .bind((settings.bind, settings.port))
   .unwrap()